본문 바로가기
알고리즘/코딩 - 프로그래머스

[Java][프로그래머스][Level 1] 시저 암호

by 주남2 2019. 4. 4.
반응형

문제 설명

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 AB는 1만큼 밀면 BC가 되고, 3만큼 밀면 DE가 됩니다. z는 1만큼 밀면 a가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.

 

제한 조건

  • 공백은 아무리 밀어도 공백입니다.
  • s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
  • s의 길이는 8000 이하입니다.
  • n은 1 이상, 25 이하인 자연수입니다.

생각

처음 내 코드를 보고 너무 억지가 아닌가 싶었는데 대부분의 사람들과 방식은 비슷했다.

 

먼저 String을 받아 split을 하고 배열에 넣는다. 후에 각 요소들이 소문자인지 대문자인지 혹은 공백인지를 구분한다. 공백이라면 그대로 넘어가지만 소문자나 대문자라면 n만큼 뒤로 민 문자를 만들어줘야 한다. 이때, 만약 z를 넘어간다면 다시 a로 돌아가야 하기 때문에 이 부분을 신경 써줘야 한다고 생각했다. 

 

처음에는 n을 더한 것이 z / Z를 넘는지 비교하여 넘어간다면 알파뱃 개수인 26만큼 빼주는 방법을 생각해봤는데, % 연산자로 하는 것이 훨씬 좋은 것 같다. 그래서 내 코드를 리뷰한 후에 좀 더 효율적으로 바꾼 코드를 다시 적어보겠다.

 

시저 암호에 대한 예를 들어보면, "aB"라는 문자와 3이라는 숫자가 들어오면 3만큼 뒤로 밀어낸 "dE"가 나와야 한다.    또는 "Z d C" 와 1이 들어오면 "A e D"가 나와야 한다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
      public String solution(String s, int n) {
          String answer = "";
          char[] s2 = new char[s.length()];
          
          //배열로 받아주는 부분이다.
          for(int i=0; i<s2.length; i++) {
              s2[i] = s.charAt(i);
          }
          
            //각 요소가 공백이라면 넘어가고, 소문자와 대문자라면 각각 n만큼 뒤로 밀어준다.
          //이 때, z나 Z를 넘어간다면 26을 빼주는 방법을 사용하였다.
          for(int i=0; i<s2.length; i++) {
              if(s2[i]==' ') {
                  continue;
              } else if(s2[i]>='a' && s2[i]<='z') {
                  if(s2[i]+> 'z') {
                      s2[i] = (char)(s2[i]+n-26);
                  } else {
                      s2[i] += n;
                  }
              } else if(s2[i]>='A' && s2[i]<='Z') {
                  if(s2[i]+> 'Z') {
                      s2[i] = (char)(s2[i]+n-26);
                  } else {
                      s2[i] += n;
                  }
              }
          }
          
          for(int i=0; i<s2.length; i++) {
              answer += s2[i];
          }
          
          
          return answer;
      }
    }
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
 

코드를 줄일 수 있는 부분이 꽤 있다. 첫 째로는 배열의 필요성이다. for문 안에 넣어서 char ch = s.char(i)로 해도 전혀 문제가 없을 것 같다. 두 번째로는 소문자인지 대문자인지 판단하는 기준이다. 물론 저 방식이 효율이 떨어지는 방식은 아니지만 Character.isLowerCase() / Character.isUpperCase()라는 함수가 있어 이것을 사용하는 것도 좋아 보인다.

마지막으로는 z / Z를 넘었을 때 26을 빼는 방식이다. 이 방법은 % 연산자를 이용하면 한 줄에 가능할 것 같아 보인다. 

(사실 고수분들의 풀이를 아주 많이 참고했다.)

수정 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
    public String solution(String s, int n) {
          String answer = "";
          
          
          for(int i=0; i<s.length(); i++) {
              char ch = s.charAt(i);
              
              if(Character.isLowerCase(ch)) {
                  ch = (char)((ch+ n%26 - 'a') % 26 + 'a');
              } else if(Character.isUpperCase(ch)) {
                  ch = (char)((ch + n%26 - 'A')% 26 + 'A');
              }
              
              answer += ch;
          }
              
          
          return answer;
      }
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
 
 

ch = (char)((ch+n%26-'a')%26 + 'a' 가 생각보다 복잡하게 나왔다. ch = 'y', n=2라고 가정해보자. n%26을 쓴 이유는 26보다 커지면 알파뱃의 주기를 넘어 한 바퀴가 돌기 때문에 26으로 나눈 나머지를 통해 항상 26보다 작은 수를 만들려고 했다.  'y' + 2%26 -'a' = 26이다. 이를 26으로 나눈 나머지를 구하면 0 따라서 ch = 'a'가 된다.

한 가지의 예를 더 들어보면 ch = 'q', n = 13이라고 해보자. 'q' + 13 - 'a' = 29가 나오고 이를 26으로 나눈 나머지는 3이다. 여기에 'a'를 더하면 'd'가 되므로 원하던 결과와 일치한다.  (사실 본인도 이해하는데 시간이 걸려 예를 2개나 들었다.. ㅎㅎ)

반응형