아스키코드의 구조를 이용하여 풀었습니다. 아스키코드에서 문자열은 영문 대문자 기준 숫자 65
부터 90
까지 A에서 Z까지의 코드를 가지고 있고, 영문 소문자 기준 숫자 97
부터 122
까지 a에서 z까지의 코드를 갖고 있습니다.
요는 기존의 코드 숫자 +1
을 한 숫자에 대한 영문을 반환하면 되는데, 영문 z
와 같이 경계점에 서있는 알파벳은 +1
을 할 경우 다시 첫번째 알파벳인 a
로 돌아와야 한다는 것입니다. 그래서 아래의 코드를 보면, if
와 else if
가 하나 있는데, 각 역할이 대문자가 Z
를 넘었는지 소문자가 z
의 범위를 넘었는지 체크하는 것입니다.
만일, 넘었다면 대문자의 경우, 64 + (코드 + n % 90)
을 문자로 변환하면 되고, 소문자의 경우, 96 + (코드 + n % 122)
를 문자로 변환하면 됩니다.
그리고 공백의 경우에는 예외케이스로 그냥 그대로 반환하면 됩니다.
let solution = (s, n) => s.split('').map(e => {
if(e === " ") return " ";
let cCode = e.charCodeAt(0);
if(cCode <= 90 && cCode + n > 90) {
return String.fromCharCode(((cCode + n) % 90) + 64);
}
else if(cCode + n > 122) {
return String.fromCharCode(((cCode + n) % 122) + 96);
}
return String.fromCharCode(cCode + n);
}).join("");
이 답의 작성자는 가히 천재적입니다
저는 전혀 이런 생각을 못했는데 답을 해석하자면
먼저 정규표현식으로 알파벳을 대소문자 관련없이 집어냅니다. 그리고 콜백함수를 이용해 문자를 replace
하는데, 이렇게 replace
하는 덕에, 따로 공백을 예외처리할 필요도 없습니다.
위에서 저의 코드의 경우에는 대문자의 경우 64
를 더하는 것과 소문자의 경우 96
을 더하는 경우를 따로 분리했지만 해당 답의 작성자는 아스키코드의 대문자가 65
부터 시작하고 소문자가 97
부터 시작한다는 점을 이용했습니다.
그래서 & 96
으로 비트연산을 하게 되면, 65~90
의 경우에는 64
를 도출하게 되고, 97~122
의 경우에는 96
을 도출하게 됩니다. 이러한 이유로 시작점에 대해 따로 하드코딩을 할 필요가 없어질 뿐더러 분기도 하나 줄일 수 있습니다.
그리고 위에 말했듯 대문자 아스키코드와 소문자 아스키코드의 시작점 숫자의 차이는 정확히 32
가 납니다. 그래서 해당 아스키 코드에 % 32
로 나머지연산을 해주게 되면, 그 알파벳이 정확히 얼마만큼 offset
을 가지고 있는지 알 수 있습니다.
이를테면, 인자로 문자 s="Y", n=3
이 왔다고 치면, c=c.charCodeAt(0)
이니 c=89
가 되고 거기에 %32
를 하면 25
가 나옵니다.
여기에 n
인 3
을 더하면 28
이 되고, 여기서 또 1을 빼면 27
이 됩니다.
이 상태에서 27%26
을 하면 1
이 나오고 이전에 89 & 96
의 결과로 64
를 구해놨기 때문에 결과는 65
에서 맨 뒤의 +1
때문에 결국 66
이 되고 B
가 결과로 나오게 됩니다.
let solution = (s, n) => s.replace(/[a-z]/ig, c => [ c = c.charCodeAt(0), String.fromCharCode((c & 96) + (c % 32 + n - 1) % 26 + 1) ][1]);