비제네르(Vigenère) 암호란?
비제네르 암호는 16세기부터 알려진 고전적인 다중 알파벳 치환 암호입니다. 모든 문자를 동일한 값만큼 밀어내는 카이사르 암호와 달리, 키(key) 문자열의 각 문자가 알파벳에서 차지하는 위치(A=0, B=1, ..., Z=25)를 개별 시프트 값으로 사용합니다. 이 글에서는 파이썬으로 비제네르 방식의 문자열 암호화 프로그램을 직접 구현해 보겠습니다.
문제 정의
소문자 알파벳으로 이루어진 문자열 text와 키 문자열 key가 주어져 있다고 가정합시다. 우리가 구해야 할 것은 text의 각 문자를 key의 대응 문자가 나타내는 오프셋만큼 오른쪽으로 민 새로운 문자열입니다. 만약 문자가 z를 넘어 범위를 벗어나면, 반대편(a)부터 다시 이어지도록 순환(wrap-around) 처리해야 합니다.
예를 들어 text = "code", key = "team"이 입력되면 출력은 "vsdq"가 됩니다.
예시 동작 과정
- c(2) + t(19) = 21 → v
- o(14) + e(4) = 18 → s
- d(3) + a(0) = 3 → d
- e(4) + m(12) = 16 → q
알고리즘 접근 방법
이 문제는 다음 단계를 따라 해결할 수 있습니다.
- 결과를 저장할 새로운 리스트
cip를 생성합니다. start에 소문자 'a'의 ASCII 코드 값(97)을 저장합니다.zip()으로text의 각 문자l과key의 각 문자k를 하나씩 짝지어 반복합니다.shift= k의 ASCII 값 − start (키 문자의 알파벳상 위치)pos= start + ((l의 ASCII 값 − start + shift) mod 26)chr(pos)로 변환한 문자를cip의 끝에 추가합니다.
cip에 담긴 문자들을 하나의 문자열로 연결하여 반환합니다.
여기서 mod 26 연산이 핵심입니다. 시프트 후 알파벳 범위(0~25)를 초과한 값을 다시 처음 위치로 되돌려, 알파벳이 순환하는 구조를 만들어 줍니다.
파이썬 구현 예제
class Solution:
def solve(self, text, key):
cip = []
start = ord('a')
for l, k in zip(text, key):
shift = ord(k) - start
pos = start + (ord(l) - start + shift) % 26
cip.append(chr(pos))
return ''.join([l for l in cip])
ob = Solution()
text = "code"
key = "team"
print(ob.solve(text, key))입력
"code", "team"
출력
vsdq
복잡도 및 참고 사항
- 시간 복잡도: O(n) — 문자열 길이에 비례하여 한 번씩 순회합니다.
- 공간 복잡도: O(n) — 결과 문자를 저장할 리스트가 필요합니다.
zip()은 두 문자열 중 더 짧은 길이까지만 반복합니다. 실제 비제네르 암호처럼key가text보다 짧은 경우에는(key * (len(text) // len(key) + 1))[:len(text)]와 같이 키를 반복해 늘린 뒤 적용하는 것이 좋습니다.