Study/Algorithm

Softeer Level 3 - [HSAT 3회 정기 코딩 인증평가 기출] 플레이페어 암호

pmh 2023. 8. 4. 00:07

문제링크

HSAT 3회 정기 코딩 인증평가 기출] 플레이페어 암호

입력1

HELLOWORLD
PLAYFAIRCIPHERKEY

출력1

EIYVRVVQBRGW

입력2

LEMONSTRAWBERRYAPPLEIUICE
WATERMELON

출력2

NALNBQEWTANRTZEZTKKOWQWUGW
 

풀이

더보기

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 변수에는 해당 알파벳의 좌표가 잘 들어가고 있는지를 잘 체크하면서 구현을 해야 했다.

 

그리고 마지막 암호화 로직에서는 정신을 단단히 차리고, 복잡한 경우에는 추가적으로 변수를 선언해 가며 진행했다.

그리고 각 조건에서 제대로 분기를 나눌 수 있도록 조건문을 잘 작성해야겠다. (조건문을 잘 못 작성해서 시간이 조금 더 걸렸다)

 

앞으로도 이런 복잡해보이는 문제들도 정신을 똑바로 차리고 과정을 하나하나 (잘 읽으면서) 구현해 나간다면 여러분들도 잘 풀 수 있을겁니다.

 

나보다 더 잘 푼 사람도 있겠지만 그래도 이런 문제를 푼게 기특해서 올렸습니다. 따끔한 지적은 언제나 환영합니다!