코딩테스트 #16 시저 암호

jakeseo_me·2020년 6월 28일
0

코딩테스트

목록 보기
16/23

문제

풀이

아스키코드의 구조를 이용하여 풀었습니다. 아스키코드에서 문자열은 영문 대문자 기준 숫자 65부터 90까지 A에서 Z까지의 코드를 가지고 있고, 영문 소문자 기준 숫자 97부터 122까지 a에서 z까지의 코드를 갖고 있습니다.

요는 기존의 코드 숫자 +1을 한 숫자에 대한 영문을 반환하면 되는데, 영문 z와 같이 경계점에 서있는 알파벳은 +1을 할 경우 다시 첫번째 알파벳인 a로 돌아와야 한다는 것입니다. 그래서 아래의 코드를 보면, ifelse 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가 나옵니다.

여기에 n3을 더하면 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]);
profile
대전에 있는 (주) 아이와즈에서 풀스택 웹개발자로 일하고 있는 서진규입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. Javascript를 좋아합니다.

0개의 댓글