문제
상근이와 선영이가 다른 사람들이 남매간의 대화를 듣는 것을 방지하기 위해서 대화를 서로 암호화 하기로 했다. 그래서 다음과 같은 대화를 했다.
- 상근: 그냥 간단히 암호화 하자. A를 1이라고 하고, B는 2로, 그리고 Z는 26으로 하는거야.
- 선영: 그럼 안돼. 만약, "BEAN"을 암호화하면 25114가 나오는데, 이걸 다시 글자로 바꾸는 방법은 여러 가지가 있어.
- 상근: 그렇네. 25114를 다시 영어로 바꾸면, "BEAAD", "YAAD", "YAN", "YKD", "BEKD", "BEAN" 총 6가지가 나오는데, BEAN이 맞는 단어라는건 쉽게 알수 있잖아?
- 선영: 예가 적절하지 않았네 ㅠㅠ 만약 내가 500자리 글자를 암호화 했다고 해봐. 그 때는 나올 수 있는 해석이 정말 많은데, 그걸 언제 다해봐?
- 상근: 얼마나 많은데?
- 선영: 구해보자!
어떤 암호가 주어졌을 때, 그 암호의 해석이 몇 가지가 나올 수 있는지 구하는 프로그램을 작성하시오.
입력
첫째 줄에 5000자리 이하의 암호가 주어진다. 암호는 숫자로 이루어져 있다.
출력
나올 수 있는 해석의 가짓수를 구하시오. 정답이 매우 클 수 있으므로, 1000000으로 나눈 나머지를 출력한다.
암호가 잘못되어 암호를 해석할 수 없는 경우에는 0을 출력한다.
내가 푼 풀이
접근방법: DP
암호를 해석하는 경우의수를 먼저 찾아보았다.
1. 주어진 암호가 1~26사이면 해석이 가능하다.
2. 암호가 10~26이면, [1, 1- ], [2, 2- ]로 경우의수가 더 늘어난다.
3. 암호에 0이 주어질 수도 있다.
0의 경우의수
1. 00: 앞 0은 10,20으로 사용되었어도, 0이 연속으로 오는경우 해석이 불가능하다.
2. 10, 20: 해석이 가능하다. 하지만 경우의수는 늘지않는다.
3. 30, 40, ... , 90: 해석이 불가능하다.
위 경우의 수를 토대로 암호의 첫번째 문구부터 마지막까지 탐색한다.
탐색할 때,
암호가 0이라면 --> 0의 경우의수를 탐색
암호가 0이 아니라면 --> 무조건 해석이 가능하고, 해석이 되더라도 이전 해석문 + [현재암호] 이므로 경우의수가 늘어나지 않는다.
현재 암호와 이전암호를 합쳐서 비교했을 때, 10이상 26이하라면
-> 2번 경우의수 처럼 경우의수가 더 늘어난다.
ex) 11 이 주어졌다면, 1을 해석했을땐 A 1가지지만, 11을 해석하면 AA와 K가 된다. (2가지)
네번째 암호 1이 주어졌을때, 이전암호와 합치면 11이된다.
이때 네번째 암호까지의 해석 경우의수는 BEA + [A], BE + [K] , YA + [A], Y +[K] 로 4가지가 된다.
dp[4] = (dp[4] +dp[3]) + (dp[4] + dp[2]) 가 된다.
세번째 암호가 주어졌을때, 이전암호와 합치면 51이되고, 이는 해석이 가능한 암호가 아니다.
세번째 암호까지의 해석 경우의 수는 이전 두번째 암호까지의 경우의수와 동일하다.
dp[3] = dp[3] + dp[2]
따라서 점화식을 다음과 나타낼 수 있다.
dp[n] = dp[n] + dp[n-1] (n > 0)
dp[n] = dp[n] + dp[n-2] (10 <= n <= 26)
코드로 구현하면 다음과 같다.
import Foundation
// 입력 받기
var str = Array(readLine()!).map{String($0)}
let n = str.count
str.insert("0", at: 0)
var alphabet = ["."]
var dp = Array(repeating: 0, count: str.count+1)
var chan = true
for i in 65...90 {
alphabet.append(String(UnicodeScalar(UInt32(i))!))
}
dp[0] = 1
// dp[n]: n자리 암호코드를 변환할 수 있는 경우의 수
// 단, 0에 예외처리를 해야함. 코드는 1~26 A~Z까지 변환 가능하므로, 0 또는 26보다 큰수는 변환이 불가.(마찬가지로 00도 불가.)
for i in 1..<str.count {
if i == 1 {
// 첫번째 수가 0이면 애초에 변환 불가
if Int(str[i])! == 0 {
dp[n] = 0
break
} else {
dp[i] = 1
}
continue
}
// 숫자가 0이라면, 세가지 경우의수가 존재
// 앞자리가 1,2인 10 20 , 앞자리가 0인 00, 앞자리가 2보다 큰수 30, 40, 50 ...
// 앞자리가 0이거나 앞자리가 2보다 큰수면 변환자체가 불가능
// 앞자리가 1,2인경우는 경우의수가 늘어나지않음. 1 -> A , 10 -> J 1가지임
if str[i] == "0" {
if str[i-1] == "0" || Int(str[i-1])! > 2 {
dp[n] = 0
break
}
} else {
dp[i] = (dp[i] + dp[i-1]) % 1000000
}
// 만약 앞자리수를 포함한 숫자가 10~26 이라면 경우의수가 늘어남. 1 -> A , 11 -> AA, L 1가지에서 두가지로 늘어난다.
if Int(str[i-1] + str[i])! <= 26 && Int(str[i-1] + str[i])! >= 10 {
dp[i] = (dp[i] + dp[i-2]) % 1000000
}
}
print(dp[n])
'코딩테스트 > 백준' 카테고리의 다른 글
BOJ-1707 이분 그래프 Swift (0) | 2024.06.03 |
---|---|
BOJ-7579 앱 Swift (0) | 2024.05.30 |
BOJ-2302 극장 좌석 Swift (0) | 2024.05.16 |
BOJ-1937 욕심쟁이 판다 Swift (0) | 2024.05.14 |
BOJ-11049 행렬 곱셈 순서 Swift (0) | 2024.05.13 |