코딩 테스트 연습/[프로그래머스] Python

[LV.3] 연습문제 > 시저 암호

duswjd_data 2025. 7. 4. 09:50

문제

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

 

프로그래머스

SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr


문제 설명

  • 시저 암호 : 어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식
  • 예시1) "AB"는 1만큼 밀면 "BC"가 되고, 3만큼 밀면 "DE"
    예시2) "z"는 1만큼 밀면 "a"
  • 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수 반환
  • 제한 조건)
    공백은 아무리 밀어도 공백
    s는 알파벳 소문자, 대문자, 공백으로만 이루어짐
    s의 길이는 8000이하
    n은 1 이상, 25이하인 자연수

풀이 흐름

1. 문자 하나씩 처리

  • 입력 문자열 s를 한 글자씩 순회하면서,
  • 알파벳이면 밀어주고, 공백이면 그대로 두기

2. 문자 밀기 (알파벳의 경우)

  • 알파벳은 유니코드(아스키) 코드로 변환해서 처리 → ord()
  • 밀고 나서 다시 문자로 변환할 때는 chr() 사용
  • 밀었을 때 Z나 z를 넘어가면 다시 A 또는 a로 돌아가야 함 → 나머지 연산(%)를 활용

3. 대문자/소문자 구분

  • if c.isupper(): 또는 if c.islower():로 구분

4. 공백은 그대로 두기

  • ' '이면 answer에 그대로 추가

정답 코드

def solution(s, n):
    answer = ''
    for i in s:
        if i == ' ':
            answer += ' '
        else:
            base = ord('A') if i.isupper() else ord('a')
            answer += chr((ord(i) - base + n) % 26 + base)
    return answer

코드 설명

    else:
        base = ord('A') if i.isupper() else ord('a')
  • 현재 문자가 알파벳일 때
    • 대문자면 `'A'`, 소문자면 `'a'`의 ASCII 코드(`ord`)를 기준점으로 사용
    • 왜 필요하냐면?
      • 알파벳을 0~25 범위로 바꾼 다음 밀고 다시 문자로 바꿔야 하기 때문
        예: `'a'` → 0, `'z'` → 25
        answer += chr((ord(i) - base + n) % 26 + base)
  • ord(i) - base
    • base는 'a' 또는 'A'
    • 이걸 빼면, 알파벳 기준으로 0~25로 정규화 가능

  •  + n
    • 몇 칸 밀건지 정해주는 부분
  • (...) % 26
    • 알파벳 26글자이므로, z를 넘어가면 다시 a로 돌아오게 함
  • + base
    • 다시 원래 문자 영역으로 복원
  •  chr(...)
    • 문자로 변환
  • 풀이 예시) 'z', n = 1 → 'a'
    → ord('z') = 122, base = 97
    → (122 - 97 + 1) % 26 = 0
    → chr(0 + 97) = 'a'

참고) 성능 측면 (대량 입력 시)

  • result += ...를 반복하면, 이건 문자열이 불변이기 때문에
    • 매번 새로운 문자열을 생성
    • 반복 횟수가 많으면 느릴 수 있음 (예: 1만 글자 이상)
  •  리스트에 .append()하고 마지막에 ''.join(...) 사용
def solution(s, n):
    result = []
    for c in s:
        if c == ' ':
            result.append(' ')
        else:
            base = ord('A') if c.isupper() else ord('a')
            result.append(chr((ord(c) - base + n) % 26 + base))
    return ''.join(result)

- 입력 길이가 작다면 굳이 사용할 필요는 없음


첫 번째 시도 (실패)

def solution(s, n):
    answer = ''
    
    for i in s:
        if i == ' ':
            result = answer.append(' ')
        else:
            if i.upper():
                first = ord('A')
            elif i.lower():
                first = ord('a')
            result = answer.append(chr(first + (ord(i)-first+n)%26))
    return result

문제점

  • result = answer.append(...)
    • str.append()는 없음 (문자열은 변경 불가이기 때문에 append() 사용 못함)
    • 그리고, append()는 리스트에 쓰는 메서드
  • 해결 방법
    • 방법 1: answer를 문자열로 유지하고 +=로 붙이기
    • 방법 2: answer를 리스트로 만들고 마지막에 ''.join(answer)로 결합

  •  if i.upper():과 elif i.lower():
    • i.upper()는 항상 문자열을 반환하므로 조건문에 쓰면 무조건 True
  • 해결 방법 
    • i.isupper()와 i.islower()를 사용

두 번째 시도 (성공. 효율 개선 필요)

def solution(s, n):
    answer = ''
    
    for i in s:
        if i == ' ':
            answer += ' '
        else:
            if i.isupper():
                first = ord('A')
            elif i.islower():
                first = ord('a')
            answer += chr(first + (ord(i)-first+n)%26)
    return answer