알고리즘 - 백준 2011 (암호코드)
문제 링크: https://www.acmicpc.net/problem/2011
2011번: 암호코드
나올 수 있는 해석의 가짓수를 구하시오. 정답이 매우 클 수 있으므로, 1000000으로 나눈 나머지를 출력한다. 암호가 잘못되어 암호를 해석할 수 없는 경우에는 0을 출력한다.
www.acmicpc.net
문제
상근이와 선영이가 다른 사람들이 남매간의 대화를 듣는 것을 방지하기 위해서 대화를 서로 암호화 하기로 했다. 그래서 다음과 같은 대화를 했다.
- 상근: 그냥 간단히 암호화 하자. A를 1이라고 하고, B는 2로, 그리고 Z는 26으로 하는거야.
- 선영: 그럼 안돼. 만약, "BEAN"을 암호화하면 25114가 나오는데, 이걸 다시 글자로 바꾸는 방법은 여러 가지가 있어.
- 상근: 그렇네. 25114를 다시 영어로 바꾸면, "BEAAD", "YAAD", "YAN", "YKD", "BEKD", "BEAN" 총 6가지가 나오는데, BEAN이 맞는 단어라는건 쉽게 알수 있잖아?
- 선영: 예가 적절하지 않았네 ㅠㅠ 만약 내가 500자리 글자를 암호화 했다고 해봐. 그 때는 나올 수 있는 해석이 정말 많은데, 그걸 언제 다해봐?
- 상근: 얼마나 많은데?
- 선영: 구해보자!
어떤 암호가 주어졌을 때, 그 암호의 해석이 몇 가지가 나올 수 있는지 구하는 프로그램을 작성하시오.
입력
첫째 줄에 5000자리 이하의 암호가 주어진다. 암호는 숫자로 이루어져 있다.
출력
나올 수 있는 해석의 가짓수를 구하시오. 정답이 매우 클 수 있으므로, 1000000으로 나눈 나머지를 출력한다.
암호가 잘못되어 암호를 해석할 수 없는 경우에는 0을 출력한다.
풀이 및 코드
처음 생각했던 풀이는 다음과 같다.
문제에 제시된 예제인 25114에 대해 풀어보면,
2 | 5 | 1 | 1 | 4 |
1 | ||||
2 | ||||
2 | ||||
4 | ||||
6 |
의 표를 작성할 수 있다.
<2>
자기 자신인 2에 해당하는 알파벳 하나만 나타내기 때문에 경우의 수는 1
<5>
앞서 2의 경우의 수에 5에 해당하는 알파벳 하나를 모든 경우에 덧붙임 -> 1 (2의 경우의 수)
25를 하나로 생각해 25에 해당하는 알파벳 하나로 경우를 추가함 -> 1 (2와 5를 한 숫자로 생각한 경우의 수)
=> 1 + 1 = 2
<1>
앞서 5의 경우의 수에 1에 해당하는 알파벳 하나를 모든 경우에 덧붙임 -> 2 (5의 경우의 수)
51은 26보다 크므로 하나로 생각할 수 없음 -> 0 (5와 1을 한 숫자로 생각한 경우의 수)
=> 2 + 0 = 2
<1>
앞서 1의 경우의 수에 1에 해당하는 알파벳 하나를 모든 경우에 덧붙임 -> 2 (1의 경우의 수)
11을 하나로 생각해 11에 해당하는 알파벳 하나로 경우를 추가함 -> 2 (1과 1을 한 숫자로 생각한 경우의 수 -> 11을 하나로 생각하면 앞에 남는 건 2와 5이기 때문에 5까지 계산된 경우의 수와 같다.)
=> 2 + 2 = 4
<4>
앞서 1의 경우의 수에 4에 해당하는 알파벳 하나를 모든 경우에 덧붙임 -> 4 (1의 경우의 수)
14를 하나로 생각해 14에 해당하는 알파벳 하나로 경우를 추가함 -> 2 (1과 4를 한 숫자로 생각한 경우의 수 -> 14를 하나로 생각하면 앞에 남는 건 2, 5, 1이기 때문에 1까지 계산된 경우의 수와 같다.)
=> 4 + 2 = 6
결국, 이를 단순하게 일반화 시키면
dp[i] = dp[i-1] (앞 숫자와 합쳤을 때 알파벳으로 나타낼 수 있으면: + dp[i-2] )
으로 나타낼 수 있다.
또한 0이 들어왔을 때에도 각종 예외처리를 해줘야 하는데, 이에 대한 방법이 굉장히 다양하다.
나는 그냥 input 배열에 저장할 때 0이 들어있으면 앞의 수에 10을 곱해 한번에 처리할 수 밖에 없도록 코드를 구현하였고, 이 과정에서 해석할 수 없는 암호에 대한 조건을 추가해 최종 코드를 완성하였다.
또한 맨처음에 짰을 때는 모든 경우의 수 값을 1000000의 나머지로 계산하지 않고 최종 값만 나머지 계산을 수행하였는데, 그렇게 하니까 27%에서 틀렸습니다 가 계속 떠서 모든 dp값을 1000000의 나머지로 수정하니 정답처리가 되었다. 문제에 대한 해석이 조금 부족했던 것 같다.
#include <iostream>
#include <string>
using namespace std;
int input[5000];
int dp[5000];
int main(){
// 입력
string inputStr;
cin >> inputStr;
int num = inputStr.size();
int newNum = 0;
for(int i, j = 0; i < num; i++, j++){
input[j] = inputStr[i] - '0'; // char to int
// 방금 들어온 input이 0이면,
// 앞 숫자에 10 곱해서 앞 인덱스에 집어넣기
if(input[j] == 0){
input[j - 1] = input[j - 1] * 10;
j--; // input index 감소
// 앞 숫자에 10 곱한 수가 26보다 크거나 0보다 작거나 같으면
// 해석할 수 없는 암호로 0 출력 후 종료
if(input[j] > 26 || input[j] <= 0){
cout << 0 << endl;
return 0;
}
}
// input 배열 size newNum으로 재정의
newNum = j + 1;
}
// dp[0]으로 만들 수 있는 조합 1개
dp[0] = 1;
// 각 elem 돌면서 dp table 업데이트
for(int i = 1; i < newNum; i++){
if(input[i] + 10 * input[i - 1] <= 26 && input[i] < 10){
if(i == 1)
dp[i] = dp[i - 1] + 1;
else
dp[i] = dp[i - 1] + dp[i - 2];
}
else
dp[i] = dp[i - 1];
dp[i] %= 1000000;
}
// 출력
cout << dp[newNum - 1] << endl;
return 0;
}
https://github.com/haram1117/AlgorithmStudy/commit/251707000a196bf4fb43aba26b2c83e9768a3734
DP2011 · haram1117/AlgorithmStudy@2517070
Show file tree Showing 7 changed files with 96 additions and 11 deletions.
github.com