숫자 12145가 주어졌을 때, 이 숫자의 각 자릿수를 영어 알파벳에 매핑하는 함수를 작성해야 한다고 가정해 보겠습니다. 매핑은 1부터 시작하는 인덱스를 기준으로 이루어지며, 1은 'a', 2는 'b', 3은 'c'와 같이 대응됩니다.
하나의 숫자는 여러 가지 방식으로 매핑될 수 있습니다. 위의 12145를 예로 들어 살펴보겠습니다.
먼저 다음과 같이 매핑할 수 있습니다.
12145 → 1,2,1,4,5 → a,b,a,d,e
또한 이렇게 매핑할 수도 있습니다.
12145 → 12,1,4,5 → l,a,d,e
그리고 다음과 같은 조합도 가능합니다.
12145 → 12,14,5 → l,n,e
이 외에도 다양한 조합이 존재하지만, 12145를 1,2,1,45로 나누는 것은 불가능합니다. 알파벳에는 45에 해당하는 매핑이 없기 때문입니다. 따라서 우리가 만들 함수는 유효한 모든 알파벳 매핑의 순열을 배열 형태로 반환해야 합니다.
구현 코드
이 문제는 재귀 함수를 활용해 해결할 수 있습니다. 숫자를 한 자리 또는 두 자리씩 잘라가며 유효한 범위(1~26)인지 확인하고, 문자열을 모두 소진하면 지금까지 쌓아온 조각들을 알파벳으로 변환해 결과 배열에 저장하는 방식입니다.
const num = 12145;
const mapToAlphabets = num => {
const numStr = '' + num;
let res = [];
const shoveElements = (left, right) => {
if (!left.length) {
res.push(right.map(el => {
return (+el + 9).toString(36);
}).join(''));
return;
};
if(+left[0] > 0){
shoveElements(left.slice(1), right.concat(left[0]));
};
if(left.length >= 2 && +(left.slice(0, 2)) <= 26){
shoveElements(left.slice(2), right.concat(left.slice(0, 2)));
};
};
shoveElements(numStr, []);
return res;
}
console.log(mapToAlphabets(num));코드 동작 원리
함수의 핵심 로직은 다음과 같습니다.
첫째, 재귀 분기 처리입니다. 남은 문자열(left)의 첫 번째 자릿수가 0보다 크면 한 자리를 잘라 진행하고, 두 자리를 잘랐을 때 그 값이 26 이하라면 두 자리를 잘라 진행합니다. 이렇게 하면 1부터 26 사이의 모든 유효한 조합을 빠짐없이 탐색할 수 있습니다.
둘째, 알파벳 변환 트릭입니다. (+el + 9).toString(36) 부분이 흥미로운데, 숫자에 9를 더한 뒤 36진수 문자열로 변환하면 1은 'a', 2는 'b'처럼 자동으로 대응됩니다. 예를 들어 1 + 9 = 10이고, 36진법에서 10은 'a'이며, 26 + 9 = 35는 'z'에 해당합니다.
셋째, 종료 조건입니다. 남은 문자열이 비어 있으면 하나의 완성된 매핑이 만들어진 것이므로, 누적된 조각들을 알파벳으로 변환해 연결한 뒤 결과 배열(res)에 추가합니다.
출력 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ 'abade', 'abne', 'aude', 'lade', 'lne' ]
즉, 12145는 총 5가지 방법으로 알파벳에 매핑될 수 있으며, 각 결과는 다음과 같은 분할에서 비롯됩니다.
- 'abade' → 1,2,1,4,5
- 'abne' → 1,2,14,5
- 'aude' → 1,21,4,5
- 'lade' → 12,1,4,5
- 'lne' → 12,14,5
이처럼 재귀를 활용하면 숫자를 알파벳으로 매핑하는 모든 가능한 조합을 깔끔하게 구할 수 있습니다.