ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [프로그래머스 3단계] 알고리즘 30. 시저 암호
    레거시/레거시-알고리즘(3) 2018. 4. 18. 14:49
    반응형

    문제 출처는 프로그래머스 알고리즘 연습 에서 볼 수 있습니다!(https://programmers.co.kr/learn/challenges)


    알고리즘 30. 시저 암호


    어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. A를 3만큼 밀면 D가 되고 z를 1만큼 밀면 a가 됩니다. 공백은 수정하지 않습니다. 보낼 문자열 s와 얼마나 밀지 알려주는 n을 입력받아 암호문을 만드는 caesar 함수를 완성해 보세요.

    “a B z”,4를 입력받았다면 “e F d”를 리턴합니다.


    #include<iostream>
    #include<string>
    using namespace std;

    string caesar(string s, int n)
    {
        string answer = "";

        return answer;
    }

    int main()
    {
        string text = "a B z";
        int testNo = 4;

        string testAnswer = caesar(text, testNo);

        cout<<testAnswer;
    }


    풀이:


    이 문제의 핵심은 간단하다. 알파벳은 26개라는 것! n이 몇이 되었건 간에 알파벳 26개를 순회하고 있다는 것을 기억하면 된다. 내가 세운 알고리즘은 다음과 같다.


    알아둘 것 : c++에서 'a' ~ 'z' 는 숫자로 97~122이다. 'A'~'Z'는 65~90이다.


    1. 주어진 문자열에서 대문자 혹은 소문자일 경우에 연산을 수행한다.
    2. 대문자건 소문자건 0~25의 범위로 만든다. 이 때 대문자라면 65를 소문자라면 97을 빼면 된다.
    3. 문자를 n번 옮긴다. 이 때 'z'(25)에서 다음 위치는 'a'(0)이다.  %연산자를 이용하면 쉽게 구할 수 있다.
    4. 다시 대/소문자에 따라 문자를 복원한다.


    코드는 다음과 같다.


    string caesar(string s, int n)
    {
        string answer = "";
    char calibrate_ch;                            //대소문자에 따른 문자 보정
    for(int i=0; i < s.size(); i++){

    if ( ('A' <= s[i] && s[i] <= 'Z') || ('a' <= s[i] && s[i] <= 'z') )   //주어진 문자열 내 대/소문자일 때 연산 수행
    {
    calibrate_ch = ('A' <= s[i] && s[i] <= 'Z') ? 'A' : 'a';    //대문자라면 65(A) 소문자라면 97(a)를 빼준다. 문자 보정
       
    s[i] -= calibrate_ch;                     //문자를 대/소문자 상관 없이 0~25의 범위로 만든다.
    s[i] = (s[i] + n) % 26;                   //알파벳 26개. 원래 위치에서 n번을 옮긴 것을 의미('z'일 때 'a'로 옮겨짐)
    s[i] += calibrate_ch;                   //대, 소문자 복원
    }
     answer += s[i];
    }
        return answer;
    }


    여기서 코드를 개선시킬 수 있는 곳은 2군데 있다. 먼저 조건식들이 보기 불편하다. is_character 라는 함수를 정의해도 좋지만 밑에 조건식 하나만 있는 것이 있으므로 함수를 2가지를 만든다.


    bool is_upper_case(char c){
    return 'A' <= c && c <= 'Z';
    }

    bool is_lower_case(char c){
    return 'a' <= c && c <= 'z';
    }


    그 후 조건식에 대하여 대입해준다. 조금 더 보기 깔끔할 것이다.


    string caesar(string s, int n)
    {
      string answer = "";
    char calibrate_ch;              //대소문자에 따른 문자 보정
    for(int i=0; i < s.size(); i++){

    if is_upper_case(s[i]) || is_lower_case(s[i]))
    {
    calibrate_ch = (is_upper_case(s[i])) ? 'A' : 'a';
     
    s[i] -= calibrate_ch;   //소문자건 대문자건 0이 된다.
    s[i] = (s[i] + n) % 26; //알파벳 26개. 원래 위치에서 n번을 옮긴 것을 의미('z'일 때 'a'로 옮겨짐)
    s[i] += calibrate_ch; //대, 소문자 복원
    }
     answer += s[i];
    }
      return answer;
    }


    그리고 문제의 핵심 문자 보정의 다음 3줄 역시 


    s[i] -= calibrate_ch;   //소문자건 대문자건 0이 된다.
    s[i] = (s[i] + n) % 26; //알파벳 26개. 원래 위치에서 n번을 옮긴 것을 의미('z'일 때 'a'로 옮겨짐)
    s[i] += calibrate_ch; //대, 소문자 복원


    다음과 같이 한 줄로 만들어서 보기 깔끔하게 만들 수 있다.


    s[i] = calibrate_ch + (s[i]-calibrate_ch+n) % 26;


    따라서 코드는 다음과 같이 변하게 된다.


    bool is_upper_case(char c){
    return 'A' <= c && c <= 'Z';
    }

    bool is_lower_case(char c){
    return 'a' <= c && c <= 'z';
    }

    string caesar(string s, int n)
    {
      string answer = "";
    char calibrate_ch;              
    for(int i=0; i < s.size(); i++){

    if is_upper_case(s[i]) || is_lower_case(s[i]))
    {
    calibrate_ch = (is_upper_case(s[i])) ? 'A' : 'a';
    s[i] = calibrate_ch + (s[i]-calibrate_ch+n) % 26
    }
     answer += s[i];
    }
      return answer;
    }


    이 문제를 풀면서 놀랐던 것은 생각보다 기존 문제와 비교하였을 때 c++로 푼 사람들 중에 깔끔하게 혹은 놀라운 방식으로 푼 사람이 없었다는.... 문제가 쉬워 그런가? 그래도 이 정도면 깔끔하게 푼 것 같다. c++을 좀 더 잘하면 더 새로운 코드가 만들어질것 같은데 말이지...

Designed by Tistory.