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

你可能感兴趣的:(java-算法,基础算法)