hihocoder好的數字串

描述

給定一個數字字符串S,如果一個數字字符串(只包含0-9,可以有前導0)中出現且只出現1次S,我們就稱這個字符串是好的。

例如假設S=666,則1666、03660666是好的,6666、66、123不是好的;假設S=1212,則01212、12123是好的,121212、121是不好的。

請你計算長度爲N的數字字符串中,有多少個是好的。由於總數可能很大,你只需要輸出總數模1000000007的餘數。

輸入

一個整數N和一個數字串S。

對於30%的數據,1 ≤ N ≤ 8

對於100%的數據,1 ≤ N ≤ 1000,1 ≤ |S| ≤ N。

輸出

一個整數代表答案。

樣例輸入

6 1212

樣例輸出

298

閱讀完題意,第一感覺是要利用動態規劃和遞推思想來完成該問題,但是如何設計動態規劃的子問題還是挺複雜的。當知道一個長度爲m的數字串的哪些信息以後,往他的尾部添加數字才能方便的往後遞推出長度爲m + 1的數字串的同類別信息呢。而且對解決當前的問題有益。首先需要知道長度爲m的數字串中有沒有出現過子串S,才方便遞推m + 1的數字串的信息,當然長度爲m的數字串的最後幾個數字的信息也是很關鍵的,它們決定了在尾部添加一個新的數字後,新生成的數字串會不會包含新的S子串。

通過分析,設計如下的子問題結構,DP(l, s_l, c) l 代表數字串的長度, s_l代表數字串從尾部開始的最長S前綴的長度, c代表數字串中已經擁有的S串的個數, DP(l, s_l, c) 代表滿足l, s_l, c的數字串的個數。
下面舉例說明DP(l, s_l, c)對應的哪類數字串信息。

S = 1231 時, 11231 屬於 DP(5, 4, 1)
S = 1231 時, 1123 屬於 DP(4, 3, 0)
S = 1231 時, 112311 屬於 DP(6, 1, 1)

等等等。

通過上述的分析,還有一個小問題需要解決,就是知道一個字符串尾部的S前綴長度,在往後面添加一個數字後,新的串的尾部字串能對應的S串的最長前綴長度,這個問題可以通過該問題的遞歸性來解決。

如果a1a2a3a4 是 S 的一個前綴字串, 如果添加的數字a5’是S的第五個數字,則a1a2a3a4a5’就是新的前綴,長度變爲5.如果a5’不是S串的第五個數字,則需要重新計算尾部的最長S前綴字串,但是在計算的過程中,可以使用a4的信息來遞歸計算。

代碼部分:

import java.Util.*;

class Const {
  public static long mod = 1000000007;
};


//計算數字串S每個位置對應的前綴長度, 如123123 的子串 12312 的第五個數字爲2,該位置對應的新子串長度爲2,
//即不從第一個S數字開始的最長前綴子串的長度
// (1 2) 3 (1 2), 第二個括號的內容,重複了S的前綴,長度爲2。
public  void calcuSubChuanLength(int[] S, int[] pre_len) {
    pre_len[0] = 0;
    pre_len[1] = 0;
    for(int i = 2; i < S.length; i++) {
        int n_loc = pre_len[i - 1];
        while(n_loc != 0 && S[n_loc + 1] != S[i]) { //計算S[i]的最長重複位置
            n_loc = pre_len[n_loc];
        }  
        if(n_loc == 0) { //前一個位置的數沒有辦法形成新的子串頭
            if(S[i] == S[1]) {
                pre_len[i] = 1;
            } else {
                pre_len[i] = 0;
            }
        } else {
            pre_len[i] = n_loc + 1;
        }
    }
    return;
}
//隨意輸入一個數組,看一下輸出的結果,下標爲0的位置,其數字沒有使用。
int[] demo_S = new int[7];
//123127
demo_S[0] = 0;
demo_S[1] = 1;
demo_S[2] = 2;
demo_S[3] = 3;
demo_S[4] = 1;
demo_S[5] = 2;
demo_S[6] = 7;
int[] demo_pre_len = new int[7];
Arrays.fill(demo_pre_len, 0);
calcuSubChuanLength(demo_S, demo_pre_len);
String demo_str = "";
String demo_ans = "";
for(int i = 1; i < demo_pre_len.length; i++) {
   demo_str += " " + demo_S[i];
   demo_ans += " " + demo_pre_len[i];
}
System.out.println(demo_str);
System.out.println(demo_ans);
 1 2 3 1 2 7
 0 0 0 1 2 0

從上面的示列中,可以形象的看出,求解的pre_len的含義,

public long slove(int[] S, int n) {
    final long mod = 100000007;
    int s_length = S.length - 1;
    long[][][] dp = new long[n + 1][s_length + 1][2];
    int[] pre_len = new int[s_length + 1];
    Arrays.fill(pre_len, 0);
    calcuSubChuanLength(S, pre_len);
    for(int i = 0; i <= n; i++) {
        for(int j = 0; j <= s_length; j++) {
            Arrays.fill(dp[i][j], 0);
        }
    }
    dp[0][0][0] = 1;
    for(int l = 0; l < n; l++) {
        for(int s_l = 0; s_l <= s_length; s_l++) {
            for(int k = 0; k <= 1; k++) {
                //對dp[l][s_l][k] 的末尾添加從0到9的數字,進行狀態遷移計算
                for(int num = 0; num <= 9; num++) {
                    if(s_l < s_length) { 
                        if(S[s_l + 1] == num) {
                            if(s_l + 1 == s_length) {
                                if(k == 1) continue;
                                else {
                                    dp[l + 1][s_l + 1][k + 1] = (dp[l + 1][s_l + 1][k + 1] + dp[l][s_l][k]) % mod;
                                }
                            } else {
                                dp[l + 1][s_l + 1][k] = (dp[l + 1][s_l + 1][k] + dp[l][s_l][k]) % mod;
                            }
                        } else {
                            int next_s_l = pre_len[s_l];
                            while(next_s_l != 0 && S[next_s_l + 1] != num) {
                                next_s_l = pre_len[next_s_l];
                            }
                            if(S[next_s_l + 1] == num) {
                                dp[l + 1][next_s_l + 1][k] = (dp[l + 1][next_s_l + 1][k] + dp[l][s_l][k]) % mod;
                            } else {
                                dp[l + 1][0][k] = (dp[l + 1][0][k] + dp[l][s_l][k]) % mod;
                            }
                        }
                    } else if(s_l == s_length) {
                        int next_s_l = pre_len[s_l];
                        while(next_s_l != 0 && S[next_s_l + 1] != num) {
                            next_s_l = pre_len[next_s_l];
                        }
                        if(S[next_s_l + 1] == num) {
                            dp[l + 1][next_s_l + 1][k] = (dp[l + 1][next_s_l + 1][k] + dp[l][s_l][k]) % mod;
                        } else {
                            dp[l + 1][0][k] = (dp[l + 1][0][k] + dp[l][s_l][k]) % mod;
                        }
                    }
                }

            }
        }
    }
    long ans = 0;
    for(int s_l = 0; s_l <= s_length; s_l++) {
        ans = (ans + dp[n][s_l][1]) % mod;
    }
    return ans;
}

對6 1212的樣例進行計算。

int[] S = new int[5];
S[0] = 0;
S[1] = 1;
S[2] = 2;
S[3] = 1;
S[4] = 2;
long ans = slove(S, 6);
System.out.println(ans);
298
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章