수학과의 좌충우돌 프로그래밍

시저암호(프로그래머스-level1) 본문

알고리즘/파이썬

시저암호(프로그래머스-level1)

ssung.k 2018. 9. 6. 23:11

안녕하세요 강민성입니다.


제목에서도 보셨겠지만 오늘 다뤄볼 주제는 '시저암호' 입니다.


시저암호는 들어본 분들도 계실꺼예요. 


시저암호는 카이사르 암호라고도 하는데 암호 중에 가장 간단한 암호라고 할 수 있습니다.


암호화 하는 방식은 문제에 나와 있으니 문제를 보며 알아보도록 하겠습니다.




[ 문제 ]

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다.

예를 들어 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



암호화 하는 과정이 단순하므로 크게 다른 설명 없이 바로 풀이를 해보도록 하겠습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def solution(s, n):
 
    answer = ""  # 빈 문자열 만들기
 
    alphabet_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # 영어 대문자들이 있는 문자열
 
    alphabet_lower = alphabet_upper.lower() # 영어소문자들이 있는 문자열
 
    length = len(s) # 주어지는 문자열의 길이
 
    for j in range(len(s)): # 문자열의 길이만큼 반복
 
        if s[j] in alphabet_upper: # 대문자에 있는 문자열에 있으면
 
            origin_index = alphabet_upper.find(s[j]) # 그 알파벳이 있는 위치를 저장
 
            changed_index = (origin_index + n) % 26 # 그 위치값에 n을 더하고 26으로 나눈  나머지를 구해 z를 벗어나고 뒤로 가는 경우도 고려해준다.
 
            answer += alphabet_upper[changed_index] # 다시 그 값에 대응되는 알파벳을 배열에 추가해준다.
 
        if s[j] in alphabet_lower: # 소문자일 경우도 반복
 
            origin_index = alphabet_lower.find(s[j])
 
            changed_index = (origin_index + n) % 26
 
            answer += alphabet_lower[changed_index]
 
        if s[j] == " "# 공백이 있으면
 
            answer = answer + " " # 공백을 더해준다.
 
    return answer
cs



코드가 굉장히 길지만 겹치는 코드가 있어서 사실은 그렇게 길지는 않습니다.


제가 생각한 방법은 다음과 같습니다.


먼저 알파벳 대문자가 순서대로 있는 문자열과 마찬가지로 소문자가 있는 문자열을 만들어줍니다.


그리고 암호화 시킬 문자열 s 가 들어오면 


대문자, 소문자, 공백 에 따라서 각각 다른 과정을 취해줍니다.


대문자나 소문자가 들어오는 경우에는 그 알파벳이 몇 번째 알파벳인지를 저장해주고


n 만큼 shift 해야하므로 n을 더해줍니다.


이 때 n 값이 커진다면 z 를 가르키는 25보다 커질 수 있으므로 


26으로 나눠주어 n 값이 아무리 커져도 값을 0~25까지의 수로 조정해줍니다.


그리고 변화된 값의 해당하는 순서의 알파벳을 answer이라는 빈 배열에 대입해줍니다.


이렇게 하다보면 공백이 사라지므로 공백이 들어왔을 경우에 answer에 공백을 추가해주도록 합니다.


코드를 짜면서 영문자가 순서대로 들어가 있는 문자열을 만들면서 다른 방법이 존재할거라 생각을 했는데


찾아보니 실제로 좋은 방법이 존재하더라고요.


바로 유니코드를 사용하는 방법입니다.


유니코드는 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 한 표준입니다.


즉 영어 알파벳들도 각각에 해당하는 숫자가 있는데 그 숫자 역시 연속적으로 나타납니다.


이를 이용해서 코드를 짜보면



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def solution(s, n):
 
    s = list(s) # s 문자열을 리스트로 만들어준다.
 
    for i in range(len(s)): 
 
        if s[i].isupper(): # 대문자라면
 
            s[i]=chr((ord(s[i])-ord('A')+ n)%26+ord('A'))

 # 위와 같은 방식을 사용하는데 ord를 통해 아스키코드로 변환한다.
 
        elif s[i].islower():
 
            s[i]=chr((ord(s[i])-ord('a')+ n)%26+ord('a'))
 
    answer = "".join(s)
 
    return answer
cs



다음과 같습니다.


논리적인 과정은 위에 과정과 같아서 낯선 함수 몇 개에 대해서 알아보도록 하겠습니다.


list(s)는 반복 가능한 자료형 s를 입력받아 리스트로 만들어 리턴하는 함수입니다.


예시를 통해 알아보면


s의 입력으로 "a B z" 가 입력된다면 ['a','','B','','z']  다음과 같이 return 되게 됩니다.

다음으로는 isupper() islower() 입니다.


두 함수는 각각 대문자인지 소문자인지 판별을 해주는 함수입니다.


isupper() 에 대해서 대문자면 True가 그렇지 않으면 False가 return 되게 됩니다.


islower() 도 마찬가지이고요.


마지막으로 join 함수 입니다.


"".join(s) 에 대해서 코드를 읽어보면 s라는 리스트를 합쳐주는데 각각의 사이에 "" 안의 값이 들어가게 됩니다.


여기서는 비어있으니까 공백이 들어가게 되겠죠.


위에 list 함수로 나눠주고 join 함수로 합쳐줘서 둘이 세트로 많이 쓰이니 알아두시면 좋을 듯 합니다.




마무리


항상 느끼는 거지만 파이썬에는 유용한 함수가 진짜 많습니다.


함수를 싹 다 외울 수는 없지만 이런 기능을 하는 함수가 있다고만 알고 있어도


찾아서 쓰면 되니 유용하게 사용할 수 있을껍니다.


오늘 알아본 4종류의 함수도 꼭 알아두셔서 유용하게 쓰시길 바랍니다.


오늘도 읽어주셔서 감사합니당!


Comments