알고리즘/BOJ

알고리즘 - 백준 2011 (암호코드)

꿀꺽람 2023. 1. 3. 17:20
반응형

문제 링크: 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

반응형