2020. 6. 6. 16:01ㆍJavascript/알고리즘
문제 설명
한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다.
각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요.
제한 사항
- numbers는 길이 1 이상 7 이하인 문자열입니다.
- numbers는 0~9까지 숫자만으로 이루어져 있습니다.
- 013은 0, 1, 3 숫자가 적힌 종이 조각이 흩어져있다는 의미입니다.
입출력 예 설명
예제 #1
[1, 7]으로는 소수 [7, 17, 71]를 만들 수 있습니다.
예제 #2
[0, 1, 1]으로는 소수 [11, 101]를 만들 수 있습니다.
- 11과 011은 같은 숫자로 취급합니다.
나의 풀이
function solution(numbers) {
let answer = 0;
let arr = numbers.split("");
let k = 1;
let i = 0;
let store = [];
arr = permutation(arr);
// 배열의 원소를 한 자리부터 n자리 까지 잘라서 정수로 변환 뒤에 store에 push
for(let i=0; i<arr.length; i++){
let j = 1;
while(j<=arr.length){
const num = arr[i].slice(0,j)/1;
//중복 체크
if(num > 1 && !store.includes(num))
store.push(num);
j++;
}
}
// 오름차순 정렬
store.sort((a,b) => a-b);
// 소수 판별
answer = primeCheck(store);
return answer;
}
function primeCheck(arr){
let count = 0;
for(let i=0; i<arr.length; i++){
let check = true;
for(let j=2; j*j<=arr[i]; j++){
if(arr[i]%j===0){
check = false;
break;
}
}
if(check){
count++;
}
}
return count;
}
function permutation(arr,store=[],idx=0){
const str = arr.join("");
if(idx === arr.length-1){
store.push(str);
return;
}
for(let i=idx; i<arr.length; i++){
let temp = arr[idx];
arr[idx] = arr[i];
arr[i] = temp;
permutation(arr, store, idx+1);
arr[i]= arr[idx];
arr[idx] = temp;
}
return store;
}
이틀 간의 대장정 끝에 겨우 푼 문제이다. 레벨 2인데 쩔쩔매다니.. 아직 많이 멀었다...
나는 네 가지를 이용하여 문제를 풀 수 있었다.
1. 순열(순열에 대해서는 이 문제를 풀기전에 한번 포스팅을 했었다(순열[이전 포스트 글]).)
2. 0, 1을 제외하고 1자리에서 n자리까지의 자연수를 중복없이 만듦.
(입력: "011"일 경우 -> [10, 11, 101, 110]으로 만들어야 한다.]
3. 중복 제거 후 정렬(오름차순)
4. 소수 체크(정확히는 약수 개수 확인)
순열을 이용해야한다는 것은 알겠으나, 아직도 재귀함수의 원리 및 순서를 잘 모르기 때문에 많이 헤맸다.
또한 , 소수를 체크하는 과정을 에라토스테네스의 체(위키백과)를 이용하여 문제를 해결하려 했으나,
실패(시간 초과)의 남발로 멘붕... 첫 날은 그대로 포기했다.
둘 째날, 무엇이 잘못됐는지 확인해보니,
이 문제의 목적은 소수를 체크하는 것이다. 하지만 내 코드는 2에서 입력받은 수까지의 소수 개수를 구하는 것이니
당연히 시간초과가 뜰수 밖에...
그래서 에라토스테네스의 체를 지워버리고
입력받은 수의 1과 자신 이외의 약수 유무를 판단하는 함수를 만들어서 소수를 체크할 수 있었다.
순열 함수의 동작 순서
이것을 쓰는 목적은 재귀 순서를 좀 더 이해하기 위해서이다.
확실히 알면 그냥 보지 않아도 된다.
입력받은 문자열 "011"을 배열로 바꿔준다.
[0, 1, 1]의 원소는 0, 1, 1이다. 이 세 개의 원소의 자리를 바꿔서 밑의 그림과 같이 서로 다른 세 자리 숫자를 만들어 줘야한다.
원소의 중복이 없으면, 독립적인 세 자리수 6개를 만들 수 있지만, 원소 1의 중복으로 독립적인 세 자리 숫자 6개를 만들 수 없다.
따라서 나는 순열과 중복을 없애는 작업을 따로 만들었다.
순열 알고리즘 동작도
내가 짠 순열 알고리즘의 순서도는 다음과 같다.
순열 알고리즘은 재귀함수를 이용한다. 재귀함수는 스택구조를 가지고 있다. 재귀함수를 이용하여 팩토리얼을 구현할 경우, 데이터 흐름도를 쉽게 파악할 수 있었지만, 순열은 어려웠다. 나는 순열의 데이터 흐름도를 파악하기 위해 출력함수를 이용하여 분석했다. 그 결과 나는 "011"을 입력받을 때 크게 세 가지 과정이 있다는 것을 알 수 있었다.
1. [0, 1, 1](idx=1)의 데이터를 스택(재귀함수)에 먼저 넣는다.
2. [0, 1, 1](idx=1)을 스왑하면 [0, 1, 1]이 두 개가 나온다. [0, 1, 1](idx=2), [0, 1, 1](idx=2)을 각각 스택에 넣는다.
3, 4. [0, 1, 1](idx=2)을 꺼내서 store배열에 넣고 store를 리턴한다.
5. [0, 1, 1](idx=1)을 꺼낸다.
나머지도 같은 작업을 거친다.
1. [1, 0, 1](idx=1)의 데이터를 스택(재귀함수)에 먼저 넣는다.
2. [1, 0, 1](idx=1)을 스왑하면 [1, 0, 1], [1, 1, 0]이 나온다. [1, 0, 1](idx=2), [1, 1, 0](idx=2)을 스택에 넣는다.
3, [1, 1, 0](idx=2)을 꺼내서 store배열에 넣고 store를 리턴한다.
4. [1, 0, 1](idx=2)을 꺼내서 store배열에 넣고 store를 리턴한다.
5. [1, 0, 1](idx=1)을 꺼낸다.
1. [1, 1, 0](idx=1)의 데이터를 스택(재귀함수)에 먼저 넣는다.
2. [1, 1, 0](idx=1)을 스왑하면 [1, 1, 0], [1, 0, 1]이 나온다. [1, 1, 0](idx=2), [1, 0, 1](idx=2)을 스택에 넣는다.
3, [1, 1, 0](idx=2)을 꺼내서 store배열에 넣고 store를 리턴한다.
4. [1, 0, 1](idx=2)을 꺼내서 store배열에 넣고 store를 리턴한다.
5. [1, 1, 0](idx=1)을 꺼낸다.
따라서 순열함수를 최종적으로 리턴하면 [011, 011, 110, 101, 101, 110]이 나온다.
참고자료
2. not yet a Developer - 약수 알고리즘(Division Algorithm)
'Javascript > 알고리즘' 카테고리의 다른 글
[JS] 프로그래머스 - 점프와 순간이동 (0) | 2020.06.18 |
---|---|
[JS] 프로그래머스 - 괄호 변환 (0) | 2020.06.16 |
[JS] 프로그래머스 - 가장 큰 수(레벨 2) (0) | 2020.06.11 |
[JS] 투 포인터, 구간 합 (0) | 2020.06.05 |
[JS] 순열 (1) | 2020.06.04 |