시저 암호
문제 링크 : https://school.programmers.co.kr/learn/courses/30/lessons/12926
문제에 대한 내용
문제 설명
어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 “AB”는 1만큼 밀면 “BC”가 되고, 3만큼 밀면 “DE”가 됩니다. “z”는 1만큼 밀면 “a”가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.
제한 조건
- 공백은 아무리 밀어도 공백입니다.
- s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
- s의 길이는 8000이하입니다.
- n은 1 이상, 25이하인 자연수입니다.
입출력 예
s | n | result |
---|---|---|
“AB” | 1 | “BC” |
“z” | 1 | “a” |
“a B z” | 4 | “e F d” |
접근 방식
- 대문자와 소문자를 각각의 리스트로 사전에 구현해 놓는다. (ex. ‘a’, ‘b’ …)
- 공백은 아무리 밀어도 공백 -> 공백이 나오면 변환 X
- 주어진 문자열을 돌면서, 대문자라면 대문자 리스트, 소문자라면 소문자리스트에서의 인덱스 위치를 구한다.
- 구한 인덱스 위치에서 n 만큼 떨어진 값을 answer에 더한다.
- answer를 리턴한다.
결과
소스 코드
import java.util.*;
class Solution {
public String solution(String s, int n) {
StringBuilder answer = new StringBuilder();
List<Character> upperAlpha = Arrays.asList('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
List<Character> lowerAlpha = Arrays.asList('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
for (char tmp:s.toCharArray()){
if (Character.isUpperCase(tmp)){
if (upperAlpha.indexOf(tmp) != -1){
char tmp2 = upperAlpha.get((upperAlpha.indexOf(tmp) + n ) % upperAlpha.size());
answer.append(tmp2);
} else {
answer.append(" ");
}
} else {
if (lowerAlpha.indexOf(tmp) != -1){
char tmp2 = lowerAlpha.get((lowerAlpha.indexOf(tmp) + n ) % lowerAlpha.size());
answer.append(tmp2);
} else {
answer.append(" ");
}
}
}
return answer.toString();
}
}
나는 무식한 방식을 선택했다. 대문자와 소문자 알파벳으로 구성된 리스트를 활용했다.
처음 문제를 읽었을 때는, 대문자와 소문자를 합치려 했으나, z에서 1을 밀면 a가 된다는 것을 보았을 때, 대문자 소문자가 서로 구분됨을 알게 되었다. 따라서, 대소문자를 구분하였다.
시저 암호의 규칙상, n은 최댓값이 25이지만 ‘z’ 에서도 25를 이동할 수 있다. 따라서 리스트의 인덱스를 벗어나지 않고 순회하기 위해 모듈러 연산을 이용했다.
결과 이미지
다른 접근 방식
다른 접근 방식이전에, 나의 코드를 최적화한다면 먼저 단순 나열을 없애고 문자열 계산을 이용하여 코드의 양을 대폭 줄일 수 있을 것 같다.
그리고, isUpperCase() 와 같은 메소드 보단, 직접적인 크기 비교를 통해 메소드 호출을 줄일 수 있을 것이다.
개선 코드
class Solution {
public String solution(String s, int n) {
StringBuilder answer = new StringBuilder(s.length());
for (char c : s.toCharArray()) {
if (c >= 'A' && c <= 'Z') {
answer.append((char) ('A' + (c - 'A' + n) % 26));
} else if (c >= 'a' && c <= 'z') {
answer.append((char) ('a' + (c - 'a' + n) % 26));
} else {
answer.append(c);
}
}
return answer.toString();
}
}
개선 된 코드를 보면, 먼저 대소문자 여부 판단을 메소드 호출이 아닌 크기 비교로 한다.
그리고, 리스트를 없애고 문자 연산을 통해 시저 암호를 구한다.
- 주어진 문자 ‘c’ 를 ‘A’ 혹은 ‘a’ 로 빼고 n을 더한다.
- 1번에서 나온 결과 값을 다시 ‘A’ 혹은 ‘a’ 에 더하고 Character로 형변환을 한다.
- 변환된 문자를 answer에 더한다.
- answer를 string으로 바꿔 리턴한다.
알게 된 점
없음.