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
",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 }
原文地址:https://www.cnblogs.com/mjtcn/p/8628711.html