[ Python ] 시저 암호

2026. 6. 25. 11:02·TIL/코테
[Programmers/Python] 시저 암호 풀이 정리
문제 링크: Programmers - 시저 암호
문자열을 한 글자씩 순회하면서 알파벳을 n칸 뒤 문자로 바꾸는 문제다. 핵심은 문자를 숫자로 바꿔 계산한 뒤 다시 문자로 변환하는 것이다.

문제 핵심

문자열 s와 정수 n이 주어졌을 때, 각 알파벳을 n칸 뒤 문자로 바꾸는 문제다. 공백은 그대로 유지하고, z 다음은 다시 a로, Z 다음은 다시 A로 순환해야 한다.

예를 들어 "AB"를 1칸 밀면 "BC"가 되고, "z"를 1칸 밀면 "a"가 된다. 따라서 단순히 문자 코드에 n을 더하는 것만으로는 부족하고, 알파벳 범위를 벗어났을 때 다시 처음으로 돌아오는 처리가 필요하다.

핵심은 문자를 숫자로 바꾼 뒤, 알파벳 위치값 기준으로 계산하고, 다시 문자로 되돌리는 것이다.

처음 막힌 지점

 처음에는 문자열을 한 글자씩 순회하는 구조까지는 만들 수 있었지만, 알파벳을 어떻게 n칸 뒤 문자로 바꾸는지 감이 잡히지 않았다. 특히 ord(), chr(), isupper()처럼 처음 보는 함수가 많아서 계산 흐름을 이해하는 데 시간이 걸렸다.
def solution(s, n):
    answer = ''
    for letter in s:

    return answer

 

 먼저 문자열을 한 글자씩 순회하는 구조를 만들고, 이후 공백 처리, 문자 코드 변환, 대소문자 구분, 알파벳 순환 처리 순서로 문제를 나누어 접근했다.

단계별 개선 과정

1. 공백 처리 구조 만들기

처음에는 문자를 무조건 answer에 추가한 뒤 공백 여부를 확인해서, 공백이 중복으로 추가되는 문제가 있었다.

def solution(s, n):
    answer = ''
    for letter in s:
        answer += letter
        if letter == ' ':
            answer += letter
    return answer

 

이후 공백인지 먼저 판단한 뒤, 공백이면 그대로 추가하고, 공백이 아니면 알파벳 변환을 하도록 구조를 바꿨다.

def solution(s, n):
    answer = ''
    for letter in s:
        if letter == ' ':
            answer += ' '
        else:
            answer += letter
    return answer
문자를 무조건 먼저 추가하지 말고, 공백인지 아닌지 판단한 뒤 추가해야 한다.

2. 문자를 숫자로 바꾸기

알파벳을 n칸 이동하려면 문자 자체에 숫자를 더할 수 없다. 그래서 ord()를 사용해 문자를 숫자 코드로 바꾼 뒤 계산해야 한다.

letter = chr(ord(letter) + n)

 

 이 방식은 A → B처럼 단순 이동은 가능하지만, z → a, Z → A처럼 알파벳 끝에서 처음으로 돌아오는 경우를 처리하지 못한다.

3. 대문자와 소문자 구분하기

 대문자와 소문자는 문자 코드 기준점이 다르다. 대문자는 A를 기준으로, 소문자는 a를 기준으로 계산해야 한다.

대문자 기준: ord('A')
소문자 기준: ord('a')

그래서 isupper()를 사용해 현재 문자가 대문자인지 확인하고, 대문자와 소문자를 나누어 계산했다.

4. 알파벳 위치값으로 변환하기

 처음에는 ord(letter) % 26처럼 문자 코드 자체에 나머지 연산을 적용하려고 했다. 하지만 이렇게 하면 A가 0번째 알파벳으로 처리되지 않는다.

잘못된 생각: ord(letter) % 26
올바른 생각: ord(letter) - ord('A') 또는 ord(letter) - ord('a')

즉, 먼저 기준 문자의 코드를 빼서 현재 문자를 0~25 사이의 알파벳 위치값으로 바꿔야 한다.

5. % 26의 위치 잡기

중간에 % 26을 마지막 전체 문자 코드에 적용해서 이상한 문자가 출력되었다. % 26은 실제 문자 코드가 아니라, 0~25 사이의 알파벳 위치값에만 적용해야 한다.

계산 흐름은 현재 위치 + n → % 26 → 기준 문자 코드 더하기 → chr() 순서다.

최종 풀이

def solution(s, n):
    answer = ''

    for letter in s:
        if letter == ' ':
            answer += ' '
        else:
            if letter.isupper():
                letter = chr(((ord(letter) - ord('A')) + n) % 26 + ord('A'))
                answer += letter
            else:
                letter = chr(((ord(letter) - ord('a')) + n) % 26 + ord('a'))
                answer += letter

    return answer

풀이 흐름

 문자열 s를 한 글자씩 순회한다. 공백이면 암호화하지 않고 그대로 answer에 추가한다. 공백이 아니라면 대문자인지 소문자인지 구분한다.

대문자라면 ord('A')를 기준으로 계산한다.
소문자라면 ord('a')를 기준으로 계산한다.

예를 들어 'Z'를 1칸 이동한다고 하면, 먼저 'Z'를 대문자 기준 위치값으로 바꾼다.

ord('Z') - ord('A') = 90 - 65 = 25
(25 + 1) % 26 = 0
0 + ord('A') = 65
chr(65) = 'A'

이렇게 하면 알파벳 끝을 넘어가더라도 다시 처음으로 돌아올 수 있다.

letter.isupper() == True처럼 써도 동작하지만, isupper() 자체가 True 또는 False를 반환하므로 보통은 letter.isupper()처럼 작성한다.

다른 풀이 : 리스트로 바꾼 뒤 수정하기

 다른 사람의 풀이에서는 문자열을 바로 누적하지 않고, 먼저 list(s)를 사용해 문자열을 문자 리스트로 바꾼 뒤 인덱스로 각 문자를 수정했다.

def caesar(s, n):
    s = list(s)

    for i in range(len(s)):
        if s[i].isupper():
            s[i] = chr((ord(s[i]) - ord('A') + n) % 26 + ord('A'))
        elif s[i].islower():
            s[i] = chr((ord(s[i]) - ord('a') + n) % 26 + ord('a'))

    return "".join(s)

이 풀이의 핵심

 파이썬 문자열은 한 글자만 직접 수정할 수 없다. 예를 들어 s[0] = 'A'처럼 문자열의 특정 위치를 바꾸는 것은 불가능하다.

그래서 이 풀이에서는 문자열을 먼저 리스트로 바꿨다. 리스트는 특정 인덱스의 값을 수정할 수 있으므로, s[i] 위치의 문자를 직접 바꿀 수 있다.

문자열: 특정 위치 직접 수정 불가
리스트: 특정 위치 직접 수정 가능

공백 처리가 짧아진 이유

 이 풀이에는 공백을 따로 처리하는 코드가 없다. 대신 if s[i].isupper(), elif s[i].islower()에 해당하지 않는 문자는 그대로 둔다. 공백은 대문자도 소문자도 아니기 때문에 아무 작업 없이 원래 값이 유지된다.

공백은 isupper()도 islower()도 아니므로 변환 대상에서 제외된다.

마지막에 join()을 사용하는 이유

 list(s)로 문자열을 리스트로 바꿨기 때문에, 결과를 반환할 때는 다시 문자열로 합쳐야 한다. 이때 사용하는 메서드가 join()이다.

return "".join(s)

"".join(s)는 리스트 안의 문자들을 빈 문자열 기준으로 이어 붙여 하나의 문자열로 만든다.

예: ['B', 'C'] → "BC"

내 풀이와 다른 풀이 비교

내 풀이: 빈 문자열 answer에 변환된 문자를 하나씩 누적한다.
다른 풀이: 문자열을 리스트로 바꾸고, 각 인덱스의 문자를 수정한 뒤 join()으로 합친다.

 두 풀이 모두 핵심 계산식은 같다. 차이는 결과 문자열을 만드는 방식이다. 처음 학습 단계에서는 answer += 문자 방식이 흐름을 이해하기 쉽고, 리스트 변환 방식은 문자열 수정 불가 특성을 이해한 뒤 익히면 좋다.

문자열을 새로 만들어 누적할 수도 있고, 리스트로 바꿔 수정한 뒤 다시 문자열로 합칠 수도 있다.

유사 유형 문제

이번에는 단순히 이동 칸 수만 바꾸는 문제가 아니라, 같은 핵심 문법을 사용하는 알파벳 반전 암호 문제를 풀었다.

알파벳 반전 암호는 다음 규칙에 따라 문자를 바꾸는 문제다.

a → z, b → y, c → x
A → Z, B → Y, C → X

공백은 그대로 유지하고, 대문자와 소문자는 각각의 범위 안에서 반전시킨다.

유사 문제 풀이

def solution(s): 
    answer = ''

    for letter in s:
        if letter == ' ':
            answer += ' '
        else:
            if letter.isupper():
                answer += chr((25 - (ord(letter) - ord('A'))) + ord('A')) 
            else:
                answer += chr((25 - (ord(letter) - ord('a'))) + ord('a')) 

    return answer

원래 문제와 같은 점

 두 문제 모두 문자열을 한 글자씩 순회하고, 공백은 그대로 유지한다. 또한 대문자와 소문자를 구분한 뒤, ord()로 문자 위치를 계산하고 chr()로 다시 문자로 바꾸는 흐름을 사용한다.

핵심 문법은 문자 순회, 공백 처리, isupper(), ord(), chr() 사용이다.

원래 문제와 다른 점

시저 암호는 현재 위치에서 n만큼 이동하는 문제이고, 알파벳 반전 암호는 현재 위치를 반대편 위치로 바꾸는 문제다.

시저 암호: (현재 위치 + n) % 26
알파벳 반전 암호: 25 - 현재 위치

핵심 정리

이번 풀이의 핵심은 문자를 숫자 위치로 바꿔 계산한 뒤, 다시 문자로 되돌리는 것이다.

 문자열 문제라고 해서 항상 문자열 메서드만 사용하는 것은 아니다. 문자를 일정한 규칙에 따라 이동시키거나 변환해야 할 때는 ord()와 chr()를 사용해 숫자 계산으로 바꾸는 것이 필요하다.

 특히 알파벳 순환 문제에서는 A 또는 a를 기준으로 현재 문자의 위치를 0~25로 변환한 뒤, 이동 또는 변환을 수행하고 다시 기준 문자 코드를 더해야 한다.

사용한 핵심 문법 정리

문자열 순회
for letter in s처럼 문자열을 한 글자씩 순회할 수 있다.
공백 판단
letter == ' '로 공백 문자를 확인한다. 공백은 암호화하지 않고 그대로 추가했다.
isupper()
문자가 대문자인지 확인한다. 대문자와 소문자는 기준 문자 코드가 다르기 때문에 구분이 필요하다.
islower()
문자가 소문자인지 확인한다. 리스트 수정 풀이에서는 대문자와 소문자를 각각 조건으로 나누기 위해 사용했다.
ord()
문자를 숫자 코드로 변환한다. 예를 들어 ord('A')는 65, ord('a')는 97이다.
chr()
숫자 코드를 다시 문자로 변환한다. 예를 들어 chr(65)는 'A'다.
% 26
알파벳은 26개이므로, 끝을 넘어가면 다시 처음으로 돌아오도록 나머지 연산을 사용한다.
기준 문자 코드 빼기
ord(letter) - ord('A') 또는 ord(letter) - ord('a')로 현재 문자를 0~25 사이의 위치값으로 바꾼다.
list()
문자열을 문자 리스트로 바꾼다. 리스트로 바꾸면 특정 인덱스의 문자를 직접 수정할 수 있다.
"".join(list)
문자 리스트를 다시 하나의 문자열로 합친다. 리스트 수정 풀이의 마지막 반환에서 사용했다.
문자열 누적
answer += 변환된_문자 형태로 변환 결과를 하나씩 이어 붙였다.
정리하면, 시저 암호 유형은 문자 자체를 바로 바꾸는 문제가 아니라 문자 코드를 이용해 위치를 계산하는 문제다. ord(), chr(), % 26의 역할을 이해하면 비슷한 알파벳 변환 문제에도 적용할 수 있다.

'TIL > 코테' 카테고리의 다른 글

[ Python ] 숫자 문자열과 영단어  (0) 2026.06.26
[ SQL ] 상위 n개 레코드  (0) 2026.06.25
[ SQL ] 아픈 동물 찾기  (0) 2026.06.24
[ Python ] 최소직사각형  (0) 2026.06.24
[ Python ] 크기가 작은 부분문자열  (0) 2026.06.23
'TIL/코테' 카테고리의 다른 글
  • [ Python ] 숫자 문자열과 영단어
  • [ SQL ] 상위 n개 레코드
  • [ SQL ] 아픈 동물 찾기
  • [ Python ] 최소직사각형
yustitix
yustitix
Semiconductor Process & Quality Data Analyst Candidate, M.S. in Polymer Engineering, @Yustitix </br> Aspiring Semiconductor Process & Quality Data Analyst, aiming to grow into a Manufacturing AI Data Scientist.
  • yustitix
    데분하다
    yustitix
  • 전체
    오늘
    어제
    • 분류 전체보기 (52)
      • Portfolio (4)
      • Job search (9)
        • JD (3)
        • Article study (5)
        • News room (1)
      • TIL (35)
        • TIL (2)
        • 코테 (11)
        • Python (6)
        • 머신러닝 (6)
        • 기초 통계 (3)
        • 반도체 공정 입문 (7)
      • Project (4)
        • FAERS 이상사례 자가보고 데이터 기반 모니터링.. (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
yustitix
[ Python ] 시저 암호
상단으로

티스토리툴바