본문 바로가기
Algorithm/Programmers

[Programmers] Lv.1 | 둘만의 암호 | Java

by unknownomad 2025. 11. 20.

문제

https://school.programmers.co.kr/learn/courses/30/lessons/155652

문제 설명

두 문자열 sskip, 그리고 자연수 index가 주어질 때, 다음 규칙에 따라 문자열을 만들려 합니다. 암호의 규칙은 다음과 같습니다.

  • 문자열 s의 각 알파벳을 index만큼 뒤의 알파벳으로 바꿔줍니다.
  • index만큼의 뒤의 알파벳이 z를 넘어갈 경우 다시 a로 돌아갑니다.
  • skip에 있는 알파벳은 제외하고 건너뜁니다.

예를 들어 s = "aukks", skip = "wbqd", index = 5일 때, a에서 5만큼 뒤에 있는 알파벳은 f지만 [b, c, d, e, f]에서 'b'와 'd'는 skip에 포함되므로 세지 않습니다. 따라서 'b', 'd'를 제외하고 'a'에서 5만큼 뒤에 있는 알파벳은 [c, e, f, g, h] 순서에 의해 'h'가 됩니다. 나머지 "ukks" 또한 위 규칙대로 바꾸면 "appy"가 되며 결과는 "happy"가 됩니다.

두 문자열 sskip, 그리고 자연수 index가 매개변수로 주어질 때 위 규칙대로 s를 변환한 결과를 return하도록 solution 함수를 완성해주세요.

 

제한사항

  • 5 ≤ s의 길이 ≤ 50
  • 1 ≤ skip의 길이 ≤ 10
  • sskip은 알파벳 소문자로만 이루어져 있습니다.
    • skip에 포함되는 알파벳은 s에 포함되지 않습니다.
  • 1 ≤ index ≤ 20

 

 

입출력 예

s skip index result
"aukks" "wbqd" 5 "happy"

 

 

입출력 예 설명

입출력 예 #1

본문 내용과 일치합니다.


풀이

class Solution {

    public String solution(String s, String skip, int index) {

        // StringBuilder는 문자열을 반복해서 붙일 때 성능이 좋음
        // (일반 문자열 + 연산보다 훨씬 빠름)
        StringBuilder answer = new StringBuilder();

        // 알파벳 a~z 총 26개이므로 크기가 26인 boolean 배열 생성
        // 예: isSkip[0] == true  → 'a'는 skip 문자
        //     isSkip[1] == true  → 'b'는 skip 문자
        boolean[] isSkip = new boolean[26];

        // skip 문자열에 포함된 문자들을 배열에 표시
        for (char c : skip.toCharArray()) {
            // 'a'를 0으로 기준으로 삼기 위해
            // 예: 'a' - 'a' = 0, 'b' - 'a' = 1 ... 'z' - 'a' = 25
            isSkip[c - 'a'] = true;
        }

        // 문자열 s의 각 문자를 하나씩 처리
        for (char ch : s.toCharArray()) {

            // index만큼 뒤로 이동한 실제 횟수 (skip 포함 X)
            int moveCount = 0;

            // 현재 문자를 숫자로 변환 (‘a’를 0으로 봄)
            // 예: ch = 'c' → current = 2
            int current = ch - 'a';

            // index만큼 'skip이 아닌' 문자들만 세며 뒤로 이동
            while (moveCount < index) {

                // 알파벳 한 칸 뒤로 이동
                current++;

                // 'z'를 넘어가면 다시 'a'로 돌아가야 하므로 26을 넘는지 확인
                // 알파벳 인덱스는 0~25까지이므로 25보다 크면 다시 0으로
                if (current > 25) {
                    current = 0;
                }

                // 이동한 위치의 알파벳이 skip에 포함되어 있으면
                // 이동 횟수(moveCount)에 포함하지 않고 그냥 넘어감
                if (isSkip[current]) {
                    // skip 문자이므로 count 증가 없이 다음 루프로 이동
                    continue;
                }

                // skip이 아닌 정상 문자면 이동 횟수 1 증가
                moveCount++;
            }

            // ---------------------------
            // !!! 매우 중요한 부분 !!!
            // current는 0~25 범위의 숫자임
            // 이 숫자를 다시 실제 알파벳 문자로 변환해야 함
            //
            // 예: current = 0 → 'a'
            //     current = 1 → 'b'
            //     current = 7 → 'h'
            //
            // 문자로 만드는 방법: (char)(숫자 + 'a')
            // 'a'는 아스키 코드 97이므로,
            // (current + 'a') 하면 -> 해당 알파벳의 아스키 코드 값이 됨.
            // 그걸 char로 바꾸면 실제 문자로 변환됨.
            // ---------------------------
            answer.append((char)(current + 'a'));
        }

        // 최종 변환된 문자열 반환
        return answer.toString();
    }
}
class Solution {
    public String solution(String s, String skip, int index) {

        // 결과 문자열을 효율적으로 만들기 위한 StringBuilder 사용
        StringBuilder result = new StringBuilder();

        // skip 에 포함된 문자를 빠르게 체크하기 위해 boolean 배열 생성
        // 알파벳 26개(a~z)를 담을 수 있음
        boolean[] skipTable = new boolean[26];

        // skip 문자열의 각 문자를 skipTable에 표시
        for (char c : skip.toCharArray()) {
            skipTable[c - 'a'] = true;
        }

        // s의 각 문자(letter)를 변환
        for (char letter : s.toCharArray()) {

            // 현재 위치에서 변환한 알파벳을 담는 변수
            char current = letter;

            // skip이 아닌 문자를 index번 이동한 횟수
            int movedCount = 0;

            // movedCount가 index에 도달할 때까지 반복
            while (movedCount < index) {

                // 알파벳을 1칸 뒤로 이동
                // 'z' 다음은 'a'로 순환되도록 처리
                current = (current == 'z') ? 'a' : (char)(current + 1);

                // current가 skipTable에 표시되어 있다면 skip 문자 → 이동 횟수 증가 X
                if (skipTable[current - 'a']) {  
                    continue;  
                }

                // skip이 아닐 경우 정상적인 이동 → 이동 횟수를 1 증가
                movedCount++;
            }

            // 변환이 끝난 current 문자를 결과에 추가
            result.append(current);
        }

        // 최종 암호화된 문자열 반환
        return result.toString();
    }
}

댓글