1 /*
2 Source :hihocoder 编程练习赛73 D 好的字符串
3 Problem :问长度为n的数字串中,有多少个包含子串s恰好一次。可以有前导0
4 Solution :DP. 利用dp[2][i][j]表示当前匹配了前i个字符,且末尾的j个字符和s的前j个字符是匹配的。然后用0,1来表示是否匹配了完整的一次s
5 对于状态转移就枚举最后一个字符,考虑和s匹配与不匹配的情况。当匹配时,dp[0][i][j] += dp[0][i][j-1] j!=m (m为s的长度),当j==m时需要转移到dp[1][i][p],p的值在下面分析。
6 当不匹配时,那么由于我们前面匹配了j的字符s[0..j-1],那么对应的位置就该是s[0..p],p满足前面的p-1的字符和s[0..j-1]后面的字符是相同的,且p是最大的。这就是next数组对应的位置。
7 所以每次我们可以枚举next数组对应的位置看是否和s[j]匹配。然后状态的转移就很容易了,可以具体看代码。
8 Date :2018-08-19-11.57
9 */
10
11 #include <bits/stdc++.h>
12 using namespace std;
13
14 typedef long long LL;
15 const int MAXN = 100005;
16 const LL MOD7 = 1e9+7;
17
18 LL dp[2][1005][1005];
19 char s[1005];
20 int _next[1005];
21 int n,m;
22
23 int getNext()
24 {
25 int i=0;
26 int j=-1;
27 _next[i]=j;
28 while (s[i])
29 {
30 if (j==-1 || s[i]==s[j]) _next[++i]=++j;
31 else j=_next[j];
32 }
33 }
34
35 void work()
36 {
37 dp[0][0][0]=1;
38 for (int i=1;i<=n;++i)
39 {
40 for (int j=1;j<=min(m,i);++j)
41 {
42 if (j<m) // 匹配一个字符
43 {
44 dp[0][i][j] += dp[0][i-1][j-1]; // < m
45 dp[1][i][j] += dp[1][i-1][j-1];
46 dp[0][i][j] %= MOD7;
47 dp[1][i][j] %= MOD7;
48 }
49 else {
50 int p=_next[m-1];
51 while (p!=-1 && s[m-1]!=s[p]) p=_next[p];
52 ++p;
53 dp[1][i][p] += dp[0][i-1][j-1];
54 dp[1][i][p] %= MOD7;
55 }
56 // 不匹配
57 for (char ch='0';ch<='9';++ch)
58 {
59 if (ch==s[j-1]) continue;
60 int p=j-1;
61 while (p!=-1 && ch!=s[p]) p=_next[p];
62 ++p;
63 dp[0][i][p] += dp[0][i-1][j-1];
64 dp[1][i][p] += dp[1][i-1][j-1];
65 dp[0][i][p] %= MOD7;
66 dp[1][i][p] %= MOD7;
67 }
68 }
69 }
70 LL ans=0LL;
71 for (int i=0;i<m;++i)
72 {
73 ans = (ans + dp[1][n][i]) % MOD7;
74 }
75 printf("%lld\n",ans);
76 }
77
78 int main()
79 {
80 #ifndef ONLINE_JUDGE
81 freopen("test.txt","r",stdin);
82 #endif // ONLINE_JUDGE
83 scanf("%d%s",&n,s);
84 m=strlen(s);
85 getNext();
86 // for (int i=0;i<m;++i) printf("%c %d\n",s[i],_next[i]);
87 work();
88 return 0;
89 }