hihocoder73 编程练习赛 D 好的字符串
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 }