내일배움캠프

[본캠프] 데이터기반 QA/QC 부트캠프 39일차

min0jun 2026. 7. 8. 15:16

1. 오늘의 학습 목표

오늘은 프로그래머스 문제 **「둘만의 암호」**를 풀면서 문자열 변환과 인덱스 이동 개념을 정리했다.

이번 문제는 문자열 s의 각 알파벳을 index만큼 뒤의 알파벳으로 바꾸되, skip에 포함된 알파벳은 건너뛰는 방식으로 암호를 만드는 문제였다.

단순히 알파벳을 몇 칸 뒤로 이동시키는 문제처럼 보이지만, 중간에 제외해야 하는 알파벳이 있고, 알파벳 끝인 z를 넘어가면 다시 a로 돌아가야 한다는 조건이 있었다.

오늘의 핵심은 다음과 같다.

  • 전체 알파벳 문자열 만들기
  • skip에 포함된 알파벳 제거하기
  • 현재 문자의 위치를 찾기
  • index만큼 이동하되 알파벳 길이를 넘어가면 다시 처음으로 돌리기
  • 나머지 연산 %를 이용해 순환 구조 처리하기

2. 오늘 학습한 내용

문제 이해

문제에서는 문자열 s, 문자열 skip, 자연수 index가 주어진다.

문자열 s의 각 문자는 index만큼 뒤의 알파벳으로 바꿔야 한다.

다만 여기서 중요한 조건이 있다.

skip에 포함된 알파벳은 변환 과정에서 제외해야 한다.

예를 들어 skip"w"가 들어 있다면, 알파벳을 이동할 때 w는 세지 않고 건너뛰어야 한다.

또한 알파벳이 z를 넘어가면 다시 a로 돌아가야 한다.

즉, 이 문제는 단순히 아스키코드로 문자를 이동하는 문제라기보다, 사용 가능한 알파벳 목록을 새로 만든 뒤 그 안에서 순환 이동하는 문제라고 볼 수 있다.

예를 들어 입력이 다음과 같다고 해보자.

s = "aukks"
skip = "wbqd"
index = 5

skip에 있는 w, b, q, d는 제외해야 한다.

그러면 사용할 수 있는 알파벳은 전체 알파벳에서 w, b, q, d를 뺀 목록이 된다.

이 목록 안에서 각 문자를 5칸씩 뒤로 이동시키면 최종 결과는 "happy"가 된다.


문제 접근

처음에는 각 문자마다 다음 알파벳으로 한 칸씩 이동하면서 skip에 있는지 확인하는 방식도 생각할 수 있다.

하지만 그렇게 하면 이동할 때마다 조건을 계속 확인해야 해서 코드가 길어질 수 있다.

그래서 먼저 사용할 수 있는 알파벳 목록을 만들어두는 방식으로 접근했다.

전체 알파벳 문자열은 다음과 같이 만들 수 있다.

alphabet = "abcdefghijklmnopqrstuvwxyz"

그다음 skip에 있는 문자를 하나씩 제거한다.

for char in skip:
    alphabet = alphabet.replace(char, "")

이렇게 하면 skip에 포함된 문자가 제거된 새로운 알파벳 목록이 만들어진다.

이제 s의 각 문자를 하나씩 확인하면서, 현재 문자가 alphabet 안에서 몇 번째 위치에 있는지 찾는다.

current_idx = alphabet.index(char)

그 후 index만큼 뒤로 이동한다.

new_idx = current_idx + index

하지만 이 상태에서는 인덱스가 알파벳 길이를 넘어갈 수 있다.

예를 들어 사용할 수 있는 알파벳 길이가 22인데, 이동한 인덱스가 25가 될 수도 있다. 이 경우 다시 처음으로 돌아가야 하므로 나머지 연산을 사용한다.

new_idx = (current_idx + index) % len(alphabet)

% len(alphabet)을 사용하면 인덱스가 알파벳 길이를 넘어가도 다시 처음부터 순환하게 만들 수 있다.

이 문제의 핵심은 바로 이 부분이었다.


풀이 코드

def solution(s, skip, index):
    answer = ""
    alphabet = "abcdefghijklmnopqrstuvwxyz"

    for char in skip:
        alphabet = alphabet.replace(char, "")  # skip에 있는 알파벳 제거

    for char in s:
        current_idx = alphabet.index(char)  # 현재 문자의 위치 찾기
        new_idx = (current_idx + index) % len(alphabet)  # index만큼 이동, 끝나면 다시 처음으로
        answer += alphabet[new_idx]

    return answer

코드 설명

먼저 결과 문자열을 담을 answer를 빈 문자열로 만든다.

answer = ""

이번 문제는 각 문자를 변환한 뒤 하나씩 이어 붙여야 하므로 리스트가 아니라 문자열로 누적해도 된다.

그다음 전체 알파벳 문자열을 만든다.

alphabet = "abcdefghijklmnopqrstuvwxyz"

이 알파벳 문자열에서 skip에 포함된 문자를 제거한다.

for char in skip:
    alphabet = alphabet.replace(char, "")

replace(char, "")는 해당 문자를 빈 문자열로 바꾸는 방식이다.

즉, skip에 들어 있는 알파벳을 제거하는 역할을 한다.

예를 들어 skip = "wbqd"라면 w, b, q, d가 알파벳 목록에서 빠진다.

그다음 s의 문자를 하나씩 순회한다.

for char in s:

각 문자가 현재 알파벳 목록에서 몇 번째 위치에 있는지 찾는다.

current_idx = alphabet.index(char)

index()는 해당 값이 문자열이나 리스트에서 몇 번째 위치에 있는지 알려준다.

그다음 현재 위치에서 index만큼 뒤로 이동한다.

new_idx = (current_idx + index) % len(alphabet)

여기서 % len(alphabet)을 사용하는 이유는 순환 구조를 만들기 위해서다.

알파벳의 끝을 넘어가면 다시 처음으로 돌아가야 하기 때문에, 나머지 연산으로 인덱스를 알파벳 길이 안에 들어오게 만든다.

마지막으로 이동한 위치의 알파벳을 answer에 더한다.

answer += alphabet[new_idx]

모든 문자를 변환한 뒤 최종 문자열을 반환한다.

return answer

풀이 정리

이번 문제는 skip을 제외한 알파벳 목록을 먼저 만드는 것이 핵심이었다.

처음부터 전체 알파벳을 기준으로 문자를 이동하면 skip 조건을 매번 확인해야 한다.

하지만 사용할 수 없는 알파벳을 미리 제거해두면, 이후에는 단순히 인덱스 이동만 하면 된다.

전체 흐름은 다음과 같다.

전체 알파벳 생성
→ skip 문자 제거
→ 현재 문자 위치 찾기
→ index만큼 이동
→ 알파벳 길이를 넘으면 처음으로 순환
→ 결과 문자열에 추가

특히 나머지 연산 %가 중요했다.

new_idx = (current_idx + index) % len(alphabet)

이 한 줄 덕분에 z를 넘어가는 상황을 따로 조건문으로 처리하지 않아도 된다.

예를 들어 인덱스가 알파벳 길이를 넘더라도, 나머지 값으로 다시 알파벳 범위 안의 인덱스를 만들 수 있다.

이번 문제는 문자열 문제이면서 동시에 순환 인덱스 처리를 연습할 수 있는 문제였다.


3. 나의 간단 소감

오늘 문제는 처음 봤을 때 조건이 꽤 많아 보였다. s를 바꿔야 하고, skip은 제외해야 하고, index만큼 이동해야 하며, z를 넘어가면 다시 a로 돌아가야 했다.

처음에는 조건을 하나씩 처리하려고 하면 코드가 복잡해질 것 같았다. 그런데 생각을 바꿔서 skip에 있는 알파벳을 먼저 제거한 새로운 알파벳 문자열을 만들어두니 훨씬 단순해졌다.

이번 문제에서 가장 크게 느낀 부분은 문제 조건을 그대로 코드로 옮기기보다, 먼저 계산하기 쉬운 형태로 데이터를 바꿔두는 것이 중요하다는 점이었다.

skip을 매번 검사하는 대신, 사용할 수 있는 알파벳만 남겨둔 뒤 그 안에서 이동하니 코드 흐름이 깔끔해졌다.

또 하나 기억에 남은 부분은 나머지 연산이었다. 알파벳 끝을 넘어가면 다시 처음으로 돌아가야 하는데, 이걸 if문으로 처리하지 않고 % len(alphabet)으로 해결할 수 있었다. 순환 구조에서는 나머지 연산이 확실히 유용하다는 걸 다시 느꼈다.

오늘 문제는 어려운 알고리즘을 쓰는 문제라기보다는, 문자열을 어떻게 다루고 인덱스를 어떻게 순환시킬지 생각하는 문제였다. 앞으로 비슷한 문제가 나오면 먼저 제외할 값은 제거하고, 반복되는 구조는 나머지 연산으로 처리할 수 있는지 확인해봐야겠다.