문제
오랜만에 프로그래머스에 들어갔는데, 새로운 문제들이 추가되었더라.
레벨1의 난이도 치고는 생각할 부분이 조금 있어 보여서, 포스팅하게 되었다.
코드
function solution(keymap, targets) {
var answer = [];
const keyPos = {};
keymap.forEach((km) => {
[...km].forEach((alphabet, pos) => {
if (!(alphabet in keyPos)) {
keyPos[alphabet] = pos + 1;
} else {
keyPos[alphabet] = Math.min(keyPos[alphabet], pos + 1);
}
});
});
targets.forEach((target) => {
let click = 0;
[...target].forEach((alphabet) => {
if (click === -1) return;
if (!(alphabet in keyPos)) {
click = -1;
} else {
click += keyPos[alphabet];
}
});
answer.push(click);
});
return answer;
}
풀이
문제조건
휴대폰 자판은 하나의 자판에 여러 문자가 할당된다.
키 하나에 여러 문자가 있으면, 빠르게 누르면서 문자를 바꾼다.
휴대폰 자판마다 할당된 키는 1~100개까지 있을 수 있다.
특정 키를 눌렀을 때 입력되는 문자도 무작위다.
같은 문자가 자판 전체에 여러번 할당될 수도 있고,
키 하나에 같은 문자가 여러 번 할당될 수도 있다.
아예 할당되지 않았을수도 있다.(= 몇몇 문자열은 작성 불가능하다.)
휴대폰 자판을 이용해 특정 문자열을 작성할 때,
키를 최소 몇번 눌러야 그 문자열을 작성할 수 있는지 알아보자.
제한사항
keymap : 1번키부터 차례대로 할당된 문자들이 순서대로 담긴 문자열 배열.
targets : 입력하려는 문자열들이 담긴 문자열배열
각 문자열 작성을 위해 "키를 최소한 몇번 눌러야하는지" 순서대로 배열에 담아 리턴.
목표달성이 불가능하면 -1 저장.
1<=keymap.length<=100
1<=keymap 원소길이<=100
keymap[i]는 i+1번 키를 눌렀을 때 바뀌는 문자이다.
keymap 원소 길이는 서로 다르고, 알파벳 대문자로만 이루어짐.
1<=targets.length<=100
1<=targets 원소 길이<=100
풀이방향
자판 클릭으로 targets에 있는 문자열을 완성시켜야 한다.
그러기 위해선 targets 문자열들의 각 문자들이 keymap 어디에 위치하는지 알아야 하는데,
keymap, targets 배열의 크기나 원소길이가 최대 100인것을 감안하면, 무지성으로 풀어도 풀릴 듯 하다.
그러나 우리는 keymap에 담긴 버튼 정보를 가지고 문자열들의 최소 클릭횟수를 알아두고 한번에 풀어보자.
이것을 keyPosition이라는 객체에 저장하겠다.
targets 문자열마다 문자열의 각 문자들의 클릭 횟수를 keyPosition 객체를 통해 알아낼 수 있다.
문자열을 완성하기 위한 각 문자들의 클릭 횟수를 더한다.
keyPosition 객체의 값들은 최소 클릭수이므로 위에서 구한 값이 해당 문자열의 클릭 최소값이고,
keyPosition에 없는 문자를 포함한다면 -1을 리턴해주면 된다.
if문을 줄여본 코드
function solution(keymap, targets) {
var answer = [];
const keyPos = {};
keymap.forEach((km) => {
[...km].forEach((alphabet, pos) => {
const curPos = keyPos[alphabet] ?? pos + 1;
keyPos[alphabet] = Math.min(curPos, pos + 1);
});
});
targets.forEach((target) => {
let click = 0;
for (let alphabet of target) {
const curPos = keyPos[alphabet] ?? -1;
if (curPos === -1) {
click = -1;
break;
}
click += curPos;
}
answer.push(click);
});
return answer;
}
여러 개의 if/else을 forEach를 break로 탈출할 수 없어 문자가 존재하지 않아도 배열을 끝까지 돌아야 하는 문제를 개선했다.
그외... 주절주절
?? 라는 문법은 nullish coalescing operator 이다.
부정값(ex. 0이라던가)일 때 사용하는 것 처럼 보이지만, undefined/null 값일 때만 후자의 값으로 할당된다.
유용한 연산자이니 mdn을 잘 읽어보고 체득하면 좋다.(현재 코드에서는 key값이 없으면 undefined를 반환하므로 사용했다.)
forEach문은 배열을 끝까지 탐색해야만 한다. 그래서 break문을 통해 forEach문을 탈출할 수가 없다.
그래서 최상단의 코드(이전 코드)에서 if(click === -1) return; 이라는 조건을 달아줬었다.
왜 for문에서는 break가 있는데, 비슷하게 동작하는 forEach에서는 break가 통하지 않을까?
for문과 forEach는 비슷해보이지만, 목적이 다르다.
파고 들어가면 꽤 복잡하지만, 맛보기 느낌으로 생각만 해보자.
for문은 절차지향적으로 동작한다.
개발자가 생각하는 흐름의 알고리즘을 for문 내부에 구현해둔다.
특정한 조건에 걸리면, break문으로 즉시 탈출시키는 등의 "절차"를 서술하는 것에 집중할 수 있다.
forEach문은 함수지향적으로 동작한다.
함수형으로 동작한다는 것은, 절차보다는 "입력"과 "결과"에 집중한다.
물론 내부에서는 어떻게 돌아가야 할지에 대한 "절차"가 기술되어 있지만 말이다.
그래서 forEach문은 "콜백함수"를 인자로 받는다.
배열의 모든 원소에 대해 이 함수를 실행한다. 가 목표다.
함수형 프로그래밍에서는 몇가지 원칙에 의해 동일한 입력에 대해서 동일한 출력을 반환해야 하므로,
break문 등을 통해 변칙적인 상황등을 만드는 상황 자체가 함수형에 적절하지 않다.
뭔가 말하다가 끊은 느낌인데,,,
조금 궁금증이 생겼다면 절차지향/객체지향/함수지향 등의 키워드로 각 방법들에 대해 관심을 가져보는 것도 좋겠다.
'DS & Algorithm > programmers' 카테고리의 다른 글
[프로그래머스] 리코쳇 로봇 - 자바스크립트 (0) | 2023.03.18 |
---|---|
[프로그래머스] 덧칠하기 - 자바스크립트 (0) | 2023.03.11 |
[프로그래머스] 억억단을 외우자 - 자바스크립트 (0) | 2022.11.25 |
[프로그래머스] 숫자 카드 나누기 - 자바스크립트 (0) | 2022.11.22 |
[프로그래머스] 섬 연결하기 - 자바스크립트 (0) | 2022.09.12 |