바로 어제 최백준 조교가 방 열쇠를 주머니에 넣은 채 깜빡하고 서울로 가 버리는 황당한 상황에 직면한 조교들은, 702호에 새로운 보안 시스템을 설치하기로 하였다. 이 보안 시스템은 열쇠가 아닌 암호로 동작하게 되어 있는 시스템이다.
암호는 서로 다른 L개의 알파벳 소문자들로 구성되며 최소 한 개의 모음(a, e, i, o, u)과 최소 두 개의 자음으로 구성되어 있다고 알려져 있다. 또한 정렬된 문자열을 선호하는 조교들의 성향으로 미루어 보아 암호를 이루는 알파벳이 암호에서 증가하는 순서로 배열되었을 것이라고 추측된다. 즉, abc는 가능성이 있는 암호이지만 bac는 그렇지 않다.
새 보안 시스템에서 조교들이 암호로 사용했을 법한 문자의 종류는 C가지가 있다고 한다. 이 알파벳을 입수한 민식, 영식 형제는 조교들의 방에 침투하기 위해 암호를 추측해 보려고 한다. C개의 문자들이 모두 주어졌을 때, 가능성 있는 암호들을 모두 구하는 프로그램을 작성하시오.
입력
첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.
출력
각 줄에 하나씩, 사전식으로 가능성 있는 암호를 모두 출력한다.
예제 입력
4 6
a t c i s w
예제 출력
acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split('\n');
const [L, C] = input[0].split(' ').map(Number);
let mo = []; // 모음
let ja = []; // 자음
input[1].split(' ').forEach((item) => {
// 모음 분리
if (['a', 'e', 'i', 'o', 'u'].includes(item)) mo.push(item);
else ja.push(item); // 자음 분리
});
// 조합 함수
const getCombination = (arr, num) => {
const results = []; // 조합 결과
if (num === 1) return arr.map((a) => [a]);
arr.forEach((fixed, index, origin) => {
const rest = origin.slice(index + 1);
const combinations = getCombination(rest, num - 1);
const attached = combinations.map((el) => [fixed, ...el]);
results.push(...attached);
});
return results;
};
const secretKeys = new Set(); // 중복 제거를 위해 집합 이용
let moCombi = getCombination(mo, 1); // 모음 1개 선택
let jaCombi = getCombination(ja, 2); // 자음 2개 선택
moCombi.map((mc) => {
jaCombi.map((jc) => {
let rest = []; // 선택된 모음 1개랑 자음 2개 제외한 나머지 값들
rest.push(...mo.filter((m) => !mc.includes(m)));
rest.push(...ja.filter((a) => !jc.includes(a)));
let restCombi = getCombination(rest, L - 3); // 모음 1개, 자음 2개 선택 후 나머지 중 선택
// 자음 2개 모음 1개로 L개 다 만들어졌을 때는 나머지 값 조합 X
if (restCombi.length === 0) secretKeys.add([...mc, ...jc].sort().join(''));
restCombi.map((rc) => {
// 모음, 자음, 나머지 정렬 후 문자열로 만들기
secretKeys.add([...mc, ...jc, ...rc].sort().join(''));
});
});
});
// 출력도 문자 순으로
[...secretKeys].sort().forEach((item) => console.log(item));
구하고자 하는 것 ⇒ 가능성 있는 새로운 보안 시스템의 암호들
풀이
모음과 자음 분리하여 입력 받기
조합 함수 이용해 모음 1개, 자음 2개 추출
추출된 모음/자음 제외한 나머지 값 중 L-3개 뽑기
⇒ 이때, 나머지를 뽑지 않아도 되는 경우 고려해서 if문으로 나머지 조합 길이가 0인 경우, 뽑은 모음과 자음으로 암호 만들기 (96%에서 틀림이 나온 원인)
사전순 정렬 후 문자열로 만들어 집합에 넣기
⇒ 중복 제거를 위해 집합 이용
출력도 사전순으로 정렬