공부/코딩테스트

[프로그래머스/js] 124 나라의 숫자 (Lv. 2)

ywwwon01 2025. 4. 8. 17:00

문제

https://school.programmers.co.kr/learn/courses/30/lessons/12899
 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

`프로그래머스 :: 124 나라의 숫자 :: Lv. 2`

문제 리뷰

숫자를 표현하는 데 있어서 1, 2, 4의 조합을 사용하는 창의적인 나라가 나옵니다.

 

저는 여느 때처럼, 일단 생각나는 풀이를 확!! 저질러 버렸는데요. (ㅋ)

그 방법은 바로

'중복 순열을 모두 구해서 n번째 수를 찾아 반환해 보자'

였습니다.

 

Q. 제한사항이 이런데, 50,000,000개가 될 수도 있는 중복 순열을 모두 구하신다고요?

A. 네!!

 

지금은 공부하는 단계니까, 이런 것도 되려나 싶은 것들도 모두 해보고 싶은 마음입니다. ㅋ

실전이라면, 안될 것 같은 로직은 바로 쳐내고 다른 아이디어를 생각하는 게 현명한 것이겠지만요

 

첫 번째 시도 (오답, 60.0/100)

code

function solution(n) {
    const nums = [1, 2, 4];
    const answer = [];
    let round = 1;
    
    while (answer.length < n) {
        answer.push(...getDupPermutations(nums, round).map(v => v.join('')));
        round += 1;
    }
    
    return answer[n - 1];
}

function getDupPermutations(arr, n) {
    let result = [];
    
    if (n === 1) return arr.map(v => [v]);
    
    arr.forEach((fixed, index, origin) => {
        const subPermutations = getDupPermutations(origin, n - 1);
        const attached = subPermutations.map(v => [fixed, ...v]);
        result.push(...attached);
    });
    
    return result;
}

 

review

그렇게 됐습니다.

 

중복 순열을 구하는 함수 자체는 제대로 동작하는 것 같고,

콜 스택이 무한하고, 배열에 무한히 많은 원소를 저장할 수 있다는 유니콘 같은 상황이라고 가정한다면,

정확도는 ㄱㅊ은 아이디어였지 않을까 생각합니다.

 

이건.. ⓐ answer 배열에 너무 많은 원소가 들어가는 문제 보다는,

getDupPermutations 함수와, 또 그 안에서 호출되는 arr.forEach()가 너무너무너무 많이 호출되어,

ⓑ 호출되는 함수의 수가 콜 스택의 한계치를 초과해 버리는 문제가 치명적이었을 것 같습니다.

 

근데 또 해보고 싶은 게 있어서 해봤습니다 (?)

그건 바로.. answer를 환형 큐로 만들어 보는 것입니다. ㅋ

 

두 번째 시도 (오답, 60.0/100)

code

const QUEUE_SIZE = 1000;

function solution(n) {
    const nums = [1, 2, 4];
    const answer = new Array(QUEUE_SIZE);
    let round = 1;
    let i = 0; // answer[i % QUEUE_SIZE]
    
    while (i < n) {
        const permutations = getDupPermutations(nums, round++).map(v => v.join(''));
        
        for (const item of permutations) {
            answer[i++ % QUEUE_SIZE] = item;
            
            if (i >= n) break;
        }
    }
    
    return answer[(i - 1) % QUEUE_SIZE];
}

function getDupPermutations(arr, n) {
    let result = [];
    
    if (n === 1) return arr.map(v => [v]);
    
    arr.forEach((fixed, index, origin) => {
        const subPermutations = getDupPermutations(origin, n - 1);
        const attached = subPermutations.map(v => [fixed, ...v]);
        result.push(...attached);
    });
    
    return result;
}

 

review

 

정답에는 가까워 질 수 없었지만,

환형 큐를 써보자는 목표는 달성할 수 있었습니다.

 

중복 순열 모두 구하는 방법 탈락!!

깔끔하게 포기!!

 

세 번째 시도 (정답)

code

function solution(n) {
    return convertTo124(n);
}

function convertTo124(n) {
    let result = [];
    const convertTable = [4, 1, 2];
    // 나머지 계산 결과(배열의 index 값에 대응)에 따른 result 출력 값 매칭
    // 3을 3으로 나누었을 때 나머지가 0 -> 4로 변환되어야 함
    // 1을 3으로 나누었을 때 나머지가 1
    // 2를 3로 나누었을 때 나머지가 2
    
    while (n > 0) {
        result = [convertTable[n % 3], ...result];
        n = n % 3 === 0 ? n - 1 : n;
        n = Math.floor(n / 3);
    }
    
    return result.join('');
}

 

review

3진법 변환 로직을 약간 변경해 적용하는 아이디어로 100점을 맞을 수 있었습니다.

 

그리고..

while 문의 탈출 처리를 위한 부분을

위와 같이 처리했는데요.

 

이런 삼항 연산자 처리 없이,

n = Math.floor((n - 1) / 3);

으로만 처리해도 정상적으로 정답이 되더군요!!

(다른 사람의 풀이를 보면서 깨닫게 되었습니다..)

 

그 이유인 즉슨,

나머지가 1이나 2일 때

(n-1) / 3 의 결과에 차이가 없으므로

한 줄의 코드로 처리가 가능하다라는 것이었습니다.

 

예를 들자면,

  • 나머지가 1인 경우
    • ex) Math.floor(16 / 3) == Math.floor(15 / 3) == 5
  • 나머지가 2인 경우
    • ex) Math.floor(17 / 3) == Math.floor(16 / 3) == 5

입니다.

 

이제 알게 됐습니다. ㅋ

 

소감

돌아돌아 모범 답안에 도달하게 되었네요.

 

이 문제를 해결하는 데 있어서 중복 순열 로직을 작성해 보는 것은 헛된 짓이었지만(ㅋ)

공부하는 차원에서 길게 본다면 뭐.

마냥 나쁜 시도는 아니었죠?

 

감사합니다.