문제링크
HSAT 3회 정기 코딩 인증평가 기출] 플레이페어 암호
입력1
HELLOWORLD
PLAYFAIRCIPHERKEY
출력1
EIYVRVVQBRGW
입력2
WATERMELON
출력2
풀이
1. 주어지는대로 입력을 받는다
plain_text = sys.stdin.readline().rstrip('\n')
enc_key = sys.stdin.readline().rstrip('\n')
2. 암호화에 쓰이는 문자열은 한번만 쓰이므로 중복된 글자를 제거한다.
enc_key = ''.join(dict.fromkeys(enc_key))
3. 암호화된 문자열을 담을 테이블을 선언하고 J를 제외한 alphabets 문자열을 선언한다.
암호화된 문자열을 담을 테이블은 어짜피 5 x 5 사이즈일테니 그냥 무식하게
enc_table = [[],[],[],[],[]]
로 선언하고,
암호화에 쓰이지 않은 알파벳들을 사용하기 위해 alphabets 문자열을 선언해서 enc_key 를 읽으면서 alphabets를 replace 함수를 통해 정리해준다
4. (☆) arr 선언
이 arr이 좀더 인간다운 풀이를 해줄 수 있도록 도와준 열쇠인데, 알파벳의 길이(26) 만큼의 0으로 초기화된 배열을 선언해준다.
그리고 이 arr에는 그 다음에 사용되는 cur 라는 변수들이 쏙쏙 들어가게 되는데 이를 이용하면
arr[ord(어떤_알파벳) - 65]
cur = [0,0] # cur[0]은 현재 행, cur[1]은 현재 열이다.
의 값을 통해 해당 문자열이 5 x 5 암호화 테이블에서 어느 위치에 있는 지 쉽게 역추적을 할 수가 있게 된다.
5. 암호화 테이블 만들기 (enc_table 안에 값 채워넣기)
# enc_key 의 알파벳부터 차례대로 순회
for alpha in enc_key:
# alphabets에서 enc_key 요소 차례대로 제거
alphabets = alphabets.replace(alpha, '')
# enc_table의 cur[0] 번째 행에 알파벳을 추가한다.
enc_table[cur[0]].append(alpha)
# arr의 ord(alpha)-65 번째에 cur 을 deep copy 한다.
arr[ord(alpha)-65] = cur.copy()
# 다음 열로 진행한다.
cur[1] += 1
# cur[1] 이 5면 다음 행부터 작성한다.
if cur[1] == 5:
cur[0] += 1
cur[1] = 0
# enc_key를 돌았다면 나머지 알파벳들을 순회
for alpha in alphabets:
# enc_table의 cur[0] 번째 행에 알파벳을 추가한다.
enc_table[cur[0]].append(alpha)
# arr의 ord(alpha)-65 번째에 cur 을 deep copy 한다.
arr[ord(alpha)-65] = cur.copy()
# 다음 열로 진행한다.
cur[1] += 1
# cur[1] 이 5면 다음 행부터 작성한다.
if cur[1] == 5:
cur[0] += 1
cur[1] = 0
6. 규칙에 맞게 차례대로 암호화를 개시해야하는데 암호화시킬 문자열을 전처리 하는 과정이 필요하다.
while(True):
# idx 가 짝수일때 그리고 idx 값이 암호화시킬 문자열의 길이보다 2작을때
# (이렇게 해야 idx+1 까지 조회할 때 오류가 안 난다)
if idx % 2 == 0 and idx < len(plain_text) - 1:
# 짝수번째 문자과 그다음 문자가 같고, 'X'가 아닐 때 => 사이에 'X' 삽입
if plain_text[idx] == plain_text[idx+1] and plain_text[idx] != 'X':
plain_text = plain_text[:idx+1] + 'X' + plain_text[idx+1:]
# continue를 하는 이유는 plain_text의 길이가 변하고
# 짝수 홀수번째 문자도 바뀌니 idx +=1 하는 과정을 생략하기 위해
continue
# 짝수번째 문자과 그다음 문자가 같고, 'X'일 때 => 사이에 'Q' 삽입
if plain_text[idx] == plain_text[idx+1] and plain_text[idx] == 'X':
plain_text = plain_text[:idx+1] + 'Q' + plain_text[idx+1:]
# continue를 하는 이유는 plain_text의 길이가 변하고
# 짝수 홀수번째 문자도 바뀌니 idx +=1 하는 과정을 생략하기 위해
continue
idx += 1
# idx 가 문자열의 끝에 도달하면 break 한다.
if idx == len(plain_text):
break
이렇게 전처리를 한 다음에는 암호화를 시작하면 된다.
7. 암호화 시작
# 전처리 완료, 암호화 시작
# 정답이 될 빈 문자열 선언
encrypted_text = ''
# plain_text 를 순차적으로 조회하기 위한 idx 변수 선언
idx = 0
# plain_text를 한 쌍씩 비교를 하기 때문에 여기서도 idx < len(plain_text) - 1의 조건을 건다.
while(idx < len(plain_text) - 1):
# 첫 번째, 두 번째 문자와, 첫 번째 문자의 순서, 두 번째 문자의 순서를 변수로 저장해둔다.
first, second = plain_text[idx], plain_text[idx+1]
first_ord, second_ord = ord(first)-65, ord(second)-65
# 1. 같은 행에 존재하는지 검사
if arr[first_ord][0] == arr[second_ord][0]:
col = arr[first_ord][0]
# 나머지 연산을 통해 다섯 번째 열일 때 첫 번째 열로 가도록 한다.
encrypted_text += enc_table[col][(arr[first_ord][1] + 1) % 5]
encrypted_text += enc_table[col][(arr[second_ord][1] + 1) % 5]
# 2. 같은 열에 존재하는지 검사
elif arr[first_ord][1] == arr[second_ord][1]:
row = arr[first_ord][1]
# 나머지 연산을 통해 다섯 번째 행일 때 첫 번째 행으로 가도록 한다.
encrypted_text += enc_table[(arr[first_ord][0] + 1) % 5][row]
encrypted_text += enc_table[(arr[second_ord][0] + 1) % 5][row]
# 3. 열이 서로 교환된 위치에 적힌 글자로 암호화
else:
encrypted_text += enc_table[arr[first_ord][0]][arr[second_ord][1]]
encrypted_text += enc_table[arr[second_ord][0]][arr[first_ord][1]]
# 문자를 두 개씩 비교하기 때문에 idx 는 2씩 증가한다.
idx+=2
코드
import sys
plain_text = sys.stdin.readline().rstrip('\n')
enc_key = sys.stdin.readline().rstrip('\n')
# enc_key의 중복된 글자를 제거한다.
enc_key = ''.join(dict.fromkeys(enc_key))
# enc_key를 이용하여 enc_table 을 만든다.
enc_table = [[],[],[],[],[]]
alphabets = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
arr = [0 for i in range(26)]
cur = [0, 0]
for alpha in enc_key:
# alphabets에서 enc_key 요소 차례대로 제거
alphabets = alphabets.replace(alpha, '')
enc_table[cur[0]].append(alpha)
arr[ord(alpha)-65] = cur.copy()
cur[1] += 1
if cur[1] == 5:
cur[0] += 1
cur[1] = 0
# print(enc_table)
for alpha in alphabets:
enc_table[cur[0]].append(alpha)
arr[ord(alpha)-65] = cur.copy()
cur[1] += 1
if cur[1] == 5:
cur[0] += 1
cur[1] = 0
# 암호화 테이블을 만들었으면 규칙에 맞게 암호화 개시
# plain_text의 전처리
# 1. plain_text에서 같은 두개의 문자가 있을 때, 사이에 X를 넣는다.
idx = 0
while(True):
# idx 가 짝수일때
if idx % 2 == 0 and idx < len(plain_text) - 1:
if plain_text[idx] == plain_text[idx+1] and plain_text[idx] != 'X':
plain_text = plain_text[:idx+1] + 'X' + plain_text[idx+1:]
continue
if plain_text[idx] == plain_text[idx+1] and plain_text[idx] == 'X':
plain_text = plain_text[:idx+1] + 'Q' + plain_text[idx+1:]
continue
idx += 1
if idx == len(plain_text):
break
# 2. 홀수 길이의 경우 마지막에 'X'를 더해준다.
if len(plain_text) % 2 != 0:
plain_text += 'X'
# 전처리 완료, 암호화 시작
encrypted_text = ''
idx = 0
while(idx < len(plain_text) - 1):
first, second = plain_text[idx], plain_text[idx+1]
first_ord, second_ord = ord(first)-65, ord(second)-65
# 1. 같은 행에 존재하는지 검사
if arr[first_ord][0] == arr[second_ord][0]:
col = arr[first_ord][0]
encrypted_text += enc_table[col][(arr[first_ord][1] + 1) % 5]
encrypted_text += enc_table[col][(arr[second_ord][1] + 1) % 5]
# 2. 같은 열에 존재하는지 검사
elif arr[first_ord][1] == arr[second_ord][1]:
row = arr[first_ord][1]
encrypted_text += enc_table[(arr[first_ord][0] + 1) % 5][row]
encrypted_text += enc_table[(arr[second_ord][0] + 1) % 5][row]
# 3. 열이 서로 교환된 위치에 적힌 글자로 암호화
else:
encrypted_text += enc_table[arr[first_ord][0]][arr[second_ord][1]]
encrypted_text += enc_table[arr[second_ord][0]][arr[first_ord][1]]
idx+=2
# 암호화된 문자열을 출력
print(encrypted_text)
느낀점
구현이 너무 빡센것같아서 중간중간 print를 해 가며 똑바로 enc_table 을 만들고 있는가, 전처리를 진행하고 있는가, arr 변수에는 해당 알파벳의 좌표가 잘 들어가고 있는지를 잘 체크하면서 구현을 해야 했다.
그리고 마지막 암호화 로직에서는 정신을 단단히 차리고, 복잡한 경우에는 추가적으로 변수를 선언해 가며 진행했다.
그리고 각 조건에서 제대로 분기를 나눌 수 있도록 조건문을 잘 작성해야겠다. (조건문을 잘 못 작성해서 시간이 조금 더 걸렸다)
앞으로도 이런 복잡해보이는 문제들도 정신을 똑바로 차리고 과정을 하나하나 (잘 읽으면서) 구현해 나간다면 여러분들도 잘 풀 수 있을겁니다.
나보다 더 잘 푼 사람도 있겠지만 그래도 이런 문제를 푼게 기특해서 올렸습니다. 따끔한 지적은 언제나 환영합니다!
'Study > Algorithm' 카테고리의 다른 글
(백준) 8983 - 사냥꾼 (0) | 2023.08.19 |
---|---|
(백준) 2954 자바 - 창영이의 일기장 (0) | 2021.08.17 |
(백준) 1182 파이썬 - 부분수열의 합 (0) | 2021.05.20 |
(백준) 12904 파이썬 - A와 B (0) | 2021.05.20 |
(백준) 1931 파이썬 - 회의실 배정 (0) | 2021.05.11 |