3670: [Noi2014]动物园
题意:给n个字符串,求出每个字符串的num值,加1后相乘。num[i]表示1~i中,有多少没有重叠的公共前缀后缀。
分析:
kmp中p数组表示最大的公共前缀后缀。设一cnt数组,表示1~i中有多少公共前缀后缀(包括重叠的),那么cnt[1]=1,(自己是自己的公共前后缀)。
那么:cnt[i]=cnt[p[i]]+1:因为1~p[i]与i-p[i]+1~i是相等的,所以1~p[i]的公共前后缀一定被包含在1~i中,且字符串1~i也是自己的公共前后缀,所以再+1
预处理出cnt数组后,那么对于1~i找到他的最大的且长度不超过i/2的公共前后缀,就是答案。
code
1 #include<cstdio> 2 #include<cstring> 3 4 const int N = 1000100; 5 const int mod = 1e9+7; 6 7 char w[N]; 8 int n,C,p[N],cnt[N]; 9 10 void work() { 11 int k,ans; 12 p[1] = 0; 13 cnt[1] = 1; 14 for (int i=2; i<=n; ++i) { 15 k = p[i-1]; 16 while (k && w[i]!=w[k+1]) k = p[k]; 17 if (w[i] == w[k+1]) k++; 18 p[i] = k; 19 cnt[i] = cnt[k] + 1; 20 } 21 k = 0,ans = 1; 22 for (int i=1; i<=n; ++i) { 23 while (k && w[i]!=w[k+1]) k = p[k];//这次求的k满足<i/2 24 if (w[i]==w[k+1]) k++; 25 //k = p[i]; //直接赋值超时,第一次求的k可能是远大于i/2的 26 while (k > (i/2)) k = p[k]; // 这次的k为下次使用,不会远大于i/2 27 ans = (1ll * ans * (cnt[k]+1)) % mod; // num[i] = cnt[k] + 1 28 } 29 printf("%d\n",ans); 30 } 31 int main () { 32 scanf("%d",&C); 33 while (C--) { 34 scanf("%s",w+1); 35 n = strlen(w+1); 36 work(); 37 } 38 return 0; 39 }