📌 문제
휴대폰의 자판은 컴퓨터 키보드 자판과는 다르게 하나의 키에 여러 개의 문자가 할당될 수 있습니다. 키 하나에 여러 문자가 할당된 경우, 동일한 키를 연속해서 빠르게 누르면 할당된 순서대로 문자가 바뀝니다.
예를 들어, 1번 키에 "A", "B", "C" 순서대로 문자가 할당되어 있다면 1번 키를 한 번 누르면 "A", 두 번 누르면 "B", 세 번 누르면 "C"가 되는 식입니다.
같은 규칙을 적용해 아무렇게나 만든 휴대폰 자판이 있습니다. 이 휴대폰 자판은 키의 개수가 1개부터 최대 100개까지 있을 수 있으며, 특정 키를 눌렀을 때 입력되는 문자들도 무작위로 배열되어 있습니다. 또, 같은 문자가 자판 전체에 여러 번 할당된 경우도 있고, 키 하나에 같은 문자가 여러 번 할당된 경우도 있습니다. 심지어 아예 할당되지 않은 경우도 있습니다. 따라서 몇몇 문자열은 작성할 수 없을 수도 있습니다.
이 휴대폰 자판을 이용해 특정 문자열을 작성할 때, 키를 최소 몇 번 눌러야 그 문자열을 작성할 수 있는지 알아보고자 합니다.
1번 키부터 차례대로 할당된 문자들이 순서대로 담긴 문자열배열 keymap과 입력하려는 문자열들이 담긴 문자열 배열 targets가 주어질 때, 각 문자열을 작성하기 위해 키를 최소 몇 번씩 눌러야 하는지 순서대로 배열에 담아 return 하는 solution 함수를 완성해 주세요.
단, 목표 문자열을 작성할 수 없을 때는 -1을 저장합니다.
📌 제한사항
- 1 ≤ keymap의 길이 ≤ 100
- 1 ≤ keymap의 원소의 길이 ≤ 100
- keymap[i]는 i + 1번 키를 눌렀을 때 순서대로 바뀌는 문자를 의미합니다.
- 예를 들어 keymap[0] = "ABACD" 인 경우 1번 키를 한 번 누르면 A, 두 번 누르면 B, 세 번 누르면 A 가 됩니다.
- keymap의 원소의 길이는 서로 다를 수 있습니다.
- keymap의 원소는 알파벳 대문자로만 이루어져 있습니다.
- 1 ≤ targets의 길이 ≤ 100
- 1 ≤ targets의 원소의 길이 ≤ 100
- targets의 원소는 알파벳 대문자로만 이루어져 있습니다.
📌 풀이
알아야 하는 것
- 문제 이해
- keymap에 들어 있는 건 마치 천지인 자판처럼 [ㄱㅋㄲ]과 같이 몇 번 누를 때마다 바뀌는 것이다.
- keymap에 만들 수 있는 targets가 없다면 -1을 저장하는 것이지 전체 -1로 반환하는 것이 아니다.
구할 것
**TC 1 : keymap = ["ABACD", "BCEFD"], targets = ["ABCD", "AABB"]
TC 2 : keymap = ["AA"], targets = ["B"]
TC 3 : keymap = ["AGZ", "BSSS"], targets = ["ASA", "BGZ"]
1) 키패드를 구성한다.
먼저 클릭을 최소화 해야 하기 때문에 TC 1처럼 B가 필요한 경우에는 1번 키맵을 2번 누르는 것보다 2번 키맵을 1번 누르는 것을 채택해야 한다. TC 3에서 S도 2번 키맵의 1번, 2번, 3번을 눌러도 동일한 S가 나오지만 최소화 해야 하기 때문에 2번 키맵을 1번 누르는 것을 채택해야 한다.
먼저 keymap을 for문으로 순회해 i번 키맵에 접근하면 pad = ABACD가 될 것이다.
이 때 padValue를 선언해 pad의 몇 번에 어떤 문자가 있는지를 확인한다.
전역 변수로 선언해둔 keymapping에 padValue가 없다면 (TC 1처럼 특정 문자를 1개 이상의 키맵이 갖고 있는 게 아니라면) 바로 넣어주되 몇 번 키맵에 몇 번째에 있는지 알기 위해 i+1, j+1를 값으로 넣어준다.
이미 같은 문자가 있다면 j+1이 더 작은 것을 골라야 하기 때문에 else if에서 조건문을 걸어주고, 더 작은 값으로 바꿔준다.
{ A: [1, 1] } 이후 B가 들어오는 것을 더 자세히 알아보자.
{ B: [1, 2] } 이 먼저 들어간 후 { B: [2, 1] } 이 후입되어 이렇게 중복이 되어 있을 때 j+1을 비교해준다.
이는 곧 keymapping["B"][1]으로, 이 부분만 바꿔주면 되기 때문에 keymapping[padValue][1] = j+1로 바꿔준다.
그래서 실제로 콘솔에 찍어보면 keymapping["B"][0]은 먼저 들어온 1이 그대로 남아 있다.
2) targets 자르기
다음 targets에 들어 있는 문자열들은 어떤 문자가 필요한지 알아본다.
targets를 for문으로 순회해 나눠주고, 횟수를 카운트할 변수 sum을 선언한다.
targets = ["ABCD", "AABB"]일 때 target = ABCD인 것이다.
이 때 target을 또 순회하는 변수 word를 선언해 A, B, C, D를 하나씩 뜯을 것이다.
만약 이 word가 keymapping에 있다면 아까 말한 것처럼 j+1 부분만 sum으로 더해주고 없다면 -1로 설정한 후 멈춰준다.
이렇게 멈추는 이유는 target에 있는 단어들 중에 keymap에 없는 게 뒤에 있다면 앞과는 무관하게 -1이 나와야 하기 때문이다.
target 하나를 다 돌았다면 answer에 sum을 push해주고, 다음 target도 동일한 방식으로 순회한다.
이후 모든 반복이 끝나고 answer를 반환하면 각 target의 클릭 횟수를 알 수 있게 된다.
function solution(keymap, targets) {
let keymapping = {};
let answer = [];
// 키패드 구성
for (let i = 0; i < keymap.length; i++) {
let pad = keymap[i];
for (let j = 0; j < pad.length; j++) {
let padValue = pad[j];
if (!keymapping[padValue]) {
keymapping[padValue] = [i + 1, j + 1];
} else if (j + 1 < keymapping[padValue][1]) {
keymapping[padValue][1] = j + 1;
}
}
}
// targets 자르기
for (let i = 0; i < targets.length; i++) {
let target = targets[i].split("");
let sum = 0;
for (let j = 0; j < target.length; j++) {
let word = target[j];
if (keymapping[word]) {
sum += keymapping[word][1];
} else {
sum = -1;
}
}
answer.push(sum);
}
return answer;
}
평소 오브젝트를 사용해서 문제를 잘 안 풀다 보니 접근하는 것 자체가 어려웠다.
하지만 오브젝트를 쓰니 편하긴 하다.
다른 분들의 풀이를 보니 reduce로 내 코드의 반만 써서 푸신 분도 계셨는데... 난 아직 멀었다리...
'⚙️ 코딩테스트' 카테고리의 다른 글
[JavaScript] 프로그래머스 코딩테스트 레벨 2 : 의상 (0) | 2023.06.11 |
---|---|
[JavaScript] 프로그래머스 코딩테스트 레벨 1 : 완주하지 못한 선수 (0) | 2023.06.09 |
[JavaScript] 프로그래머스 코딩테스트 레벨 2 : 기능 개발 (추가) (0) | 2023.06.07 |
[JavaScript] 프로그래머스 코딩테스트 레벨 1 : 둘만의 암호 (0) | 2023.06.07 |
[JavaScript] 프로그래머스 코딩테스트 레벨 1 : 과일 장수 (0) | 2023.06.06 |