시저 암호

자라나는 ㅇㅅㅇ개발자·2023년 11월 13일
0

TIL

목록 보기
112/126

프로그래머스 알고리즘 문제를 풀던 도중

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

이런 문제가 주어졌다.

직관적으로 봤을 때 신경 써야 하는 부분들이 있는 거 같아 우선 세부적으로 단계를 나누어보았다.


  1. 문자열의 각 알파벳을 아스키코드로 변환하기 위해 문자열 배열로 변환
  2. 각 알파벳마다 아스키코드로 변환
  3. 모든 인덱스에 n을 더하기
    3-1. 대문자일 경우, 소문자일 경우
    3-2. 각각 'Z'와 'z'를 넘어가서 다시 'A'와 'a'로 돌아가는 경우
  4. 아스키코드 배열을 다시 문자열로 변환

일단은 정리한 것들을 하나하나 코드로 풀어보았다.

public class Test_36_CaesarCipher {
    public String solution(String s, int n) {
        // 아스키코드 정수 배열로 변환시키기 위해 문자열 배열로 변환
        String[] sArr = s.split("");
        // 전부 아스키코드로 숫자로 변환
        int[] intArr = new int[sArr.length];
        for (int i = 0; i < sArr.length; i++) {
            intArr[i] = sArr[i].charAt(0);
        }
        // 모두 n만큼 밀기
        for (int i = 0; i < intArr.length; i++) {
            if (intArr[i] >= 65 && intArr[i] <= 90) { // 대문자일 경우
                intArr[i] += n;
                if (intArr[i] > 90) {
                    intArr[i] -= 26;
                }
            } else if (intArr[i] >= 97 && intArr[i] <= 122) { // 소문자일 경우
                intArr[i] += n;
                if (intArr[i] > 122) {
                    intArr[i] -= 26;
                }
            }
        }
        // 아스키코드 배열을 문자열로 변환
        char[] charArr = new char[intArr.length];
        for (int i = 0; i < intArr.length; i++) {
            charArr[i] = (char) intArr[i];
        }

        return new String(charArr);
    }
}

다행히 코드는 제대로 실행되는 것을 확인했다.


하지만
코드가 굉장히 지저분해보이는게 마음에 들지 않았고, 변태적이게는 아니더라도 조금이라도 개선시켜보고싶어 chat gpt의 힘을 빌려보았다.

우선 가장 먼저 눈에 띈 것은 '3. 상수 활용' 이었다.
char인 알파벳은 아스키코드로 정수형과 사칙연산이 가능하다는 간단한 것을 까먹고있었다.

		// 모두 n만큼 밀기
        for (int i = 0; i < intArr.length; i++) {
            if (intArr[i] >= 'A' && intArr[i] <= 'Z') { // 대문자일 경우
                intArr[i] += n;
                if (intArr[i] > 'Z') {
                    intArr[i] -= 26;
                }
            } else if (intArr[i] >= 'a' && intArr[i] <= 'z') { // 소문자일 경우
                intArr[i] += n;
                if (intArr[i] > 'z') {
                    intArr[i] -= 26;
                }
            }
        }

그리고 예전 내일배움캠프 수업에서 %를 사용해 나머지를 활용하는 예제에서 나왔던 방법을 사용해 if문을 조금 더 개선시켜보았다.

		// 모두 n만큼 밀기
        for (int i = 0; i < intArr.length; i++) {
            if (intArr[i] >= 'A' && intArr[i] <= 'Z') { // 대문자일 경우
                intArr[i] = (intArr[i] - 'A' + n) % 26 + 'A';
            } else if (intArr[i] >= 'a' && intArr[i] <= 'z') { // 소문자일 경우
                intArr[i] = (intArr[i] - 'a' + n) % 26 + 'a';
            }
        }

마지막으로 gpt의 힘을 빌려보았는데
내가 split으로 나누고 for문으로 문자열을 배열로 변환시키는 과정을

char[] charArr = s.toCharArray();

을 이용해 과감하게 생략해버렸다.

정말 아차싶었다...


최종 코드

public class Test_36_CaesarCipher {
    public String solution(String s, int n) {
        char[] charArr = s.toCharArray();

        for (int i = 0; i < charArr.length; i++) {
            if (charArr[i] >= 'A' && charArr[i] <= 'Z') { // 대문자 범위
                charArr[i] = (char) ((charArr[i] - 'A' + n) % 26 + 'A');
            } else if (charArr[i] >= 'a' && charArr[i] <= 'z') { // 소문자 범위
                charArr[i] = (char) ((charArr[i] - 'a' + n) % 26 + 'a');
            }
        }

        return new String(charArr);
    }
}

어깨가 조금은 올라갈만큼 만족스러운 코드이다.

0개의 댓글