링크: https://programmers.co.kr/learn/courses/30/lessons/42839
한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다.
각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요.
제한사항
입출력 예
| numbers | return |
|---|---|
| "17" | 3 |
| "011" | 2 |
입출력 예 설명
예제 #1
[1, 7]으로는 소수 [7, 17, 71]를 만들 수 있습니다.
예제 #2
[0, 1, 1]으로는 소수 [11, 101]를 만들 수 있습니다.
11과 011은 같은 숫자로 취급합니다.
이 문제는 '순열'과 '에라토스테네스의 체'를 알아야 쉽게 접근이 가능했다.
처음에는 순열이라는 개념을 생각하지 않고, for문을 돌려 문제를 해결하려고 하였지만 어려움이 많아 순열의 개념에 대해 학습하고 다시 문제에 도전하게 되었다.
링크: https://velog.io/@proshy/JS순열조합중복순열-구하기
순열: 순서대로 뽑아서 줄을 세우는 것
순열의 코드는 다음과 같다.
function permutation(arr, selectNum) {
let result = [];
// selectNum이 1이면 arr 각 자신들만 return 해주면 된다.
if (selectNum === 1) return arr.map((v) => [v]);
arr.forEach((v, idx, arr) => {
const fixer = v;
// 현재 arr에서 돌아가고 있는 v값을 제외한 나머지 배열을 resArr에 저장
const restArr = arr.filter((_, index) => index !== idx);
// 재귀함수 사용해 반복되는 부분을 처리
const permuationArr = permutation(restArr, selectNum - 1);
// 고정된 fixer 값을 기준으로 v값들을 한 배열에 넣기
const combineFixer = permuationArr.map((v) => [fixer, ...v]);
result.push(...combineFixer);
});
return result;
}

// true로 가득 채워진 빈 배열 생성(0부터 시작하기에 n+1 범위를 사용)
let primeNumbers = new Array(n + 1).fill(true);
for (let i = 0; i * i <= n + 1; i += 1) {
// 0,1은 소수가 아니다
i < 2 && (primeNumbers[i] = false)
// 자신이 소수일 때,
if (primeNumbers[i]) {
// for문을 돌려서,
for (let j = i * i; j <= n + 1; j += i) {
// 자신의 배수들을 모두 false로 만든다.
primeNumbers[j] = false;
}
}
}
이렇게 두 기반 지식을 가지고 드디어 소수 찾기 문제를 풀어 볼 시간이다.
간략하게 설명하자면
numbers를 모두 쪼개서 배열에 넣고, 이 배열을 순열을 통해 모든 경우의 수를 구해주었다.
이렇게 나온 결과는 [["1","2"],["1","3]]과 같이, 배열과 문자로 되어있었는데,
join()을 통해 배열 내부의 문자들을 합치고
Number()메서드를 통해 이러한 문자들을 숫자로 변환해 주었다.
이러한 수들을 에라토스테네스 체와 비교하여 소수를 찾아주었다.
저장된 배열에는 중복된 값들이 들어있었는데, set을 통해 중복을 없애주고,
.size로 set의 길이를 구해, 결과를 return 해주었다.
코드는 다음과 같다.(주석 첨부되어있음)
function solution(numbers) {
// numbers 쪼개기
numbers = numbers.split('')
let answer = [];
// 순열을 통한 결과값 구하기
function permutation(arr, selectNum) {
let result = [];
if (selectNum === 1) return arr.map((v) => [v]);
arr.forEach((v, idx, arr) => {
const fixer = v;
const restArr = arr.filter((_, index) => index !== idx);
const permuationArr = permutation(restArr, selectNum - 1);
let combineFixer = permuationArr.map((v) => [fixer, ...v]);
result.push(...combineFixer);
});
return result;
}
// 반복문을 통해 조각을 합치는 개수에 따른 결과값들을 배열에 저장
let piece = []
for (var i = 1; i <= numbers.length; i++) {
piece.push(permutation(numbers, i))
}
// 순열로 나온 경우의 '문자 배열'들을 '숫자'로 바꾸어주는 작업
for (var i = 0; i < piece.length; i++) {
for (var j = 0; j < piece[i].length; j++) {
answer.push(Number(piece[i][j].join('')))
}
}
// 소수를 찾는 범위를 max값으로 한정시키기 위해 max값 구하기
let max = answer.reduce((acc, cur) => acc > cur ? acc : cur)
// 소수 구하기(에라토스테네스의 체)
let primeNumbers = new Array(max + 1).fill(true);
for (let i = 0; i * i <= max + 1; i += 1) {
i < 2 && (primeNumbers[i] = false)
if (primeNumbers[i]) {
for (let j = i * i; j <= max + 1; j += i) {
primeNumbers[j] = false;
}
}
}
// 구한 값이 소수인지 확인하는 작업
answer = answer.filter(a => primeNumbers[a] === true)
// 중복 값 제거, 뒤늦게 넣어준 이유는 set 상태에서 여러 메서드들 사용 불가하기 때문에
answer = new Set(answer)
return answer.size
}