DP 문제이다.
0이 나온 경우, 예외를 고려해서 문제를 풀어나가는 게 포인트라고 생각한다!
11205210 암호코드를 예시로 규칙을 찾아보겠다.
암호코드를 두 자릿수로 끊어서 확인.
1-1. 끊은 숫자가 10~26 사이인 경우 dp[i-1]과 dp[i-2] 를 더한 값을 dp[i]에 저장
ex) 1 1 2 0... 에서 1,1 혹은 11의 경우가 나옴
그 외의 경우 dp[i]에 dp[i-1] 값을 저장.
현재 숫자가 0인 경우, 이전 숫자를 확인.
3-1) 이전 숫자가 1 또는 2가 아닌 경우, 잘못된 암호코드이므로 0 출력 후 프로그램 종료.
3-2) 이전 숫자가 1 또는 2인 경우, dp[i]에 dp[i-2] 값을 저장.
0은 단독으로 존재할 수 없어서 10 또는 20 같은 단일 암호로 사용되기 때문
계산 과정
| 인덱스 | 현재 숫자 | dp | 
|---|---|---|
| 2 | 11 | dp[2] = dp[1]+dp[0] = 2 | 
| 3 | 12 | dp[3] = dp[2]+dp[1] = 3 | 
| 4 | 0 | dp[4] = dp[2] = 2 | 
| 5 | 5 | dp[5] = dp[4] = 2 | 
| 6 | 52 | dp[6] = dp[5] = 2 | 
| 7 | 21 | dp[7] = dp[6]+dp[5] = 4 | 
| 8 | 0 | dp[8] = dp[6] = 2 | 
👀 왜 위와 같이 dp[i] 값을 결정하는가?
11205210를 예시로 보자.
1 - | 1 |
11 - | 1,1 | 11 |
112- | 1,12 | 1,1,2 | 11,2 |
1120 - | 1,1,20 | 11,20 |
11205 - | 1,1,20,5 | 11,20,5 |
112052 - | 1,1,20,5,2 | 11,20,5,2 |
1120521 - | 1,1,20,5,21 | 11,20,5,21 | 1,1,20,5,2,1 | 11,20,5,2,1 |
11205210 - | 1,1,20,5,2,10 | 11,20,5,2,10 |
- 112인 경우, 1에서 12을 추가한 경우, 11에서 2를 추가한 경우와 같으므로 dp[4]=dp[3]+dp[2]= 3
 - 1120인 경우, 숫자 20만 허용되므로, 11인 경우에 20을 추가한 경우와같으므로
 
dp[5]=dp[3]= 2- 11205인 경우, 1120의 경우에 5를, 112에 05를 추가한 경우지만, 05는 존재할 수 없으므로 1120에 5를 추가한 경우만 허용된다. dp[6]=dp[4]= 2
 위 규칙대로 과정을 반복하면 dp[8] = 2가 된다.
입력 및 예외 처리
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	n = br.readLine();
	int len = n.length();
	long[] dp = new long[len + 2];
	if(n.charAt(0)=='0'){
		System.out.println(0);
        return;
	}
        ...
변수 n에 암호코드를 입력받는다.
만일, 맨 앞자리가 0으로 시작한다면 잘못된 암호이므로 0을 출력하고 프로그램을 종료한다.
암호 코드 해독하기
	    else {
            dp[0] = 1;
            dp[1] = 1;
            for(int i=2;i<=len;i++){
                if(n.charAt(i-1)=='0') {
                    char prev = peek(i - 2);
                    if (next == '1' || next == '2') {
                        dp[i] = dp[i - 2] % 1000000;
                    } else {
                        System.out.println(0);
                        return;
                    }
                }
                else {
                    int next = Integer.parseInt(n.substring(i-2, i));
                    if (next > 9 && next < 27) {
                        dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000;
                    } else {
                        dp[i] = dp[i - 1] % 1000000;
                    }
                }
            }
        }
현재 숫자가 0인 경우, 앞전 숫자를 확인한다.
1 또는 2라면 dp[i-2] 값을 저장하고 그렇지 않다면 잘못된 암호코드이므로 0을 출력한 후 프로그램을 종료한다.
현재 숫자가 0이 아닌 경우 두자리 수를 가져온다. 10~26 사이의 숫자인 경우 dp[i-1]+dp[i-2] 값을 저장하고, 그렇지 않은 경우 dp[i-1] 값을 저장한다.
public class N_2011 {
    static String n;
    static public char peek(int idx){
        if(idx > n.length()-1) return '0';
        return n.charAt(idx);
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = br.readLine();
        int len = n.length();
        long[] dp = new long[len + 2];
        if(n.charAt(0)=='0'){
            System.out.println(0);
            return;
        }
        else {
            dp[0] = 1;
            dp[1] = 1;
            for(int i=2;i<=len;i++){
                if(n.charAt(i-1)=='0') {
                    char next = peek(i - 2);
                    if (next == '1' || next == '2') {
                        dp[i] = dp[i - 2] % 1000000;
                    } else {
                        System.out.println(0);
                        return;
                    }
                }
                else {
                    int next = Integer.parseInt(n.substring(i-2, i));
                    if (next > 9 && next < 27) {
                        dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000;
                    } else {
                        dp[i] = dp[i - 1] % 1000000;
                    }
                }
            }
        }
            System.out.println(dp[len]%1000000);
    }
}
0에 관한 예외처리를 고려하는 게 오래 걸렸다ㅠ
삽질을 하다가 도저히 안 되겠어서 참고에 올린 다른 분의 코드를 보고 이해했다.
처음에 중복조합으로 풀어야 하나.. 생각을 했는데 아무래도 아니었다.
아직 골드5의 dp는 어렵다. 그래도 열심히 해야지!