※ 본 사진과 해당 게시글 내용의 문제 모두 프로그래머스[Programmers]사이트에 발췌해왔습니다.

💬 들어가기 앞서..

💢 시저 암호 란?


카이사르 암호(Caesar cipher)라 불리기도 하는 시저 암호.
암호학에서 다루는 간단한 "치환암호"의 일종이다.
(실제로 로마의 황제 카이사르가 사용했다 전해진다.)

이 암호는 암호화할 내용을 알파벳별로 일정한 거리만큼 밀어서 다른 알파벳으로 치환하는 방식이다.
예를 들어 3글자씩 밀어내는 카이사르 암호로
'COME TO ROME'을 암호화하면 'FRPH WR URPH' 가 된다.
'RUSQHUVKBVEHQIIQIYDQJEH' 라는 암호를 해독하면
'BECAREFULFORASSASINATOR'(암살자를 조심하라)가 된다
여기서 밀어내는 글자 수는 암호를 보내는 사람과 함께 정해 더 어려운 암호를 만들 수 있다. = 순환 암호

단, 암호화할 내용에도 분명 반복되거나 꼭 쓰여야하는 품사어구가 있기 때문에
철자의 빈도와 자주 사용되는 단어와 형태를 이용하면
쉽게 풀 수 있다는 단점이 있다.
※ 출처 ㅣ 위키백과_시저암호


❓ 문제

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 
예를 들어 "AB"는 1만큼 밀면 "BC"가 되고, 3만큼 밀면 "DE"가 됩니다. 
"z"는 1만큼 밀면 "a"가 됩니다. 
문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.

제한 조건 :
공백은 아무리 밀어도 공백입니다.
s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
s의 길이는 8000이하입니다.
n은 1 이상, 25이하인 자연수입니다.

<입출력 >
s	| n	| result
"AB"	| 1	| "BC"
"z"	| 1	| "a"
"a B z"	| 4	| "e F d"

❗ 풀이

My Code

def solution(s, n):
    ls = []
    for a in s :
        if a.isspace() : 
            ls.append(a)
        else :
            ascii = ord(a)
            if ascii >= 65 and ascii < 91:
                if (ascii+n) >= 91 :
                    ascii = (ascii+n) - 26
                    new_w = chr(ascii)
                else :
                    new_w = chr(ascii+n)
            elif ascii >= 97 and ascii < 123 :
                if (ascii+n) >= 123 :
                    ascii = (ascii+n) - 26
                    new_w = chr(ascii)
                else :
                    new_w = chr(ascii+n)
            ls.append(new_w)
    return ''.join(ls)

복잡하게 해결한 감이 없지않다..
ASCII Code(아스키 코드)를 활용해서 풀었는데
Python의 chr()함수와 ord()함수는 이 아스키 코드를 활용한 함수인데
먼저 chr()함수는 인수로 정수(int)값을 받는다. 해당 정수에 해당하는 문자를 반환하는 함수이다.
ord()함수는 반대로 인수로 문자(str)값을 받고 해당 문자의 코드값을 반환한다.

위 함수들의 특징을 활용하여
우선 입력받은 문자열을 for문으로 한 글자씩 if~else문에 적용시켰다.
(바보같이 공백도 분리해주는 list()함수를 활용하지 못했다...)

여기서 중요한 점은
공백이 있을 수 있기 때문에 공백은 .isspace()로 필터링 하여
반복문 돌리지 않고 바로 결과값 도출을 위한 ls에 곧바로 append를 해주었다.

공백이 아닌 경우,
ascii라는 변수에 ord()함수를 사용해 해당 문자의 ascii 코드값을 저장한다.

이후 이중조건문을 걸었는데
먼저 대문자일 경우(if ascii >= 65 and ascii < 91:)를 살펴보자.
시저암호의 특성인 일정간격을 더해 밀어낸 결과가 Z를 넘었을 경우,
A부터 다시 시작해야하기 때문에
if (ascii+n) >= 91 :로 필터링하여 (ascii + n)에서 26을 빼주어 재선언 해준 후, chr()로 다시 알파벳을 반환한 값을 new_w라는 변수에 대입한다.
Z를 넘지 않는 나머지는 그대로 chr(ascii + n)를 대입한다.

소문자의 경우(elif ascii >= 97 and ascii < 123 :)도 비슷한 방법으로 해결하면 된다.

그렇게 new_w값들을 for문 마지막에 ls에 append한 후,
''.join(ls)를 리턴(Return)하면 암호화한 시저 암호 값이 반환된다.

※ 출처 ㅣ구글링하면 쉽게 찾을 수 있는 이재욱 님이 제작하신 ASCII코드표


❣ 다른 풀이

(1)

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)

해당 풀이는 나와 동일하게 ASCII 코드를 통한 ord()함수, chr()함수를 활용하였다.
나의 풀이보다 간결한 이유는
우선 list()함수를 사용하여
for문의 범위를 range(len(list(s))를 사용했다.

간단하게 .isupper()로 대문자의 경우를, islower()로 소문자의 경우를 구분하고
각 경우에 적합한 Aaord()값을 활용하였다.

그리고 두가지 경우에 해당하지 않는 경우,
공백일 땐 아무 변경사항이 없고

for문으로 수정이 완료된 리스트 s를 공백 없이 "".join(s) 시켜 return한다.

(2)

import string
def caesar(s, n):
    result = ""
    base = ""
    for c in s:
        if c in string.ascii_lowercase:
            base = string.ascii_lowercase
        elif c in string.ascii_uppercase:
            base = string.ascii_uppercase
        else:
            result += c
            continue
        a = base.index(c) + n
        result += base[a % len(base)]
    return result

우선 아직 나에겐 익숙치 않은 string 모듈을 사용한 풀이다.
이 string 모듈엔 신기하게도
이 문제를 위해 존재하듯이
string.ascii_lowercase / string.ascii_uppercase 라는 집합들이 있고
이에 대해 in이라는 방법으로 각 알파벳들이 어디에 해당하는지 확인했다.

for문에 들어가기 앞서 resultbase라는 빈 문자열 변수를 생성하고
for문 안에서 base에는 위 조건문에 해당하는 경우에 따라 해당하는 집합을 적용시키고
a라는 변수에 base.index(암호내용 알파벳) + n 입력해준 후,
result에 순서대로 base[a % len(base)] (a변수에 집합길이를 나눈 나머지 _ z를 넘어가는 경우 방지) 한 값을 누적시켜 입력한다.

else 조건의 경우는 바로 result += c ; continue로 누적시키는 명령이 있는데
이는 공백에 대한 명령이다.

이렇게 모두 누적이 완료된 result 변수를 return한다.

0개의 댓글