예전에 유사한 알파벳 인덱스 건너뛰기 문제에서 'z' 다음의 알파벳을 고를 경우 나머지 연산자('%')로 해결했던 기억이 났다.
Set과 spread 연산자로 알파벳 배열을 만들어주고 (['a', 'b', 'c', ...] 이렇게 치기 귀찮기 때문에) '%' 연산자로 나눠주는 알파벳의 전체 개수(26개)는 변하지 않는 값이므로 상수(const)로 선언했다.
공백은 그대로 이어붙이고, 공백이 아닐 경우 각 소문자, 대문자 배열에서 s[i]의 인덱스를 찾아 뒤로 민 새로운 인덱스를 구했다. 인덱스를 찾는 indexOf 코드가 lower, upper 각각 2번 이상 반복되어 변수로 따로 담아 사용했다.
function solution(s, n) {
let answer = '';
let lower = [...new Set('abcdefghijklmnopqrstuvwxyz')];
let upper = [...new Set('ABCDEFGHIJKLMNOPQRSTUVWXYZ')];
const TOTAL_ALPHABET_COUNT = 26;
for (let i = 0; i < s.length; i++) {
if (s[i] === ' ') answer += ' '
else {
let lowerIndex = lower.indexOf(s[i]);
let upperIndex = upper.indexOf(s[i]);
if (upperIndex !== -1) {
answer += upper[Math.floor((upperIndex + n) % TOTAL_ALPHABET_COUNT)];
}
if (lowerIndex !== -1) {
answer += lower[Math.floor((lowerIndex + n) % TOTAL_ALPHABET_COUNT)];
}
}
}
return answer;
}
점수가 4점이 떠서 혹시나 해서 indexOf 메서드의 시간 복잡도를 찾아보니 O(N)이었다. (s의 각 문자로 하나씩 찾으면 최악의 경우 알파벳 배열을 거의 끝까지 돌 수 있음 -> 'xyz'이라던지 'XYZ'이라던지..))
하지만 알파벳 배열은 길이가 정해져 있는 배열이고 그리 길지도 않다. (26개) 만약 10000개 넘어가면 고민을 해봐야 할 듯
코드 개선법으로는 이진 검색(binary search)이 떠오르긴 했는데, 실제로 짜보니 소문자와 대문자의 경우를 각각 구하는 과정에서 else-if문을 너무 많이 쓰게 되었다. 다른 더 좋은 방법이 있을지 모르겠으나 indexOf와 includes를 안 쓰는 방법은 험난했다. (가독성 Noooo..)
다른 풀이에서는 소문자 대문자 (심지어 공백까지) 일일이 컴마로 이어서 하나의 배열을 만드신 분을 봤는데 나는 이 과정에서 휴먼 에러를 안 낼 자신이 없어😂 그냥 Set과 spread에 의지한 코드를 선택했다.
=== -1
부분이 마음에 안 들어서 includes 사용도 고려해봤는데, indexOf는 인덱스를 구함과 동시에 includes 여부도 판별할 수 있고 어차피 인덱스를 뒤로 미는 과정에서 구체적인 인덱스가 필요해서 (+ 어차피 둘 다 시간복잡도 O(N)) 그냥 indexOf 가지고 풀었다.