BZOJ 3670 NOI2014 动物园 KMP+dp
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3670
题意概述:令num[i]表示字符串由1~i的字符形成的前缀中不相重叠的相同前后缀的数量,求mul{ num[i] | 1<=i<=L }mod1000000007。
实际上只要对KMP理解的好这就是个水题,可以想到dp求得num数组,num[i]=num[f[i]]+1,f[i]表示字符串的前缀1~i形成的不重叠的最长相同前后缀长度,如果f[i]=0的话就不存在。题目的提醒实际上很明显,想想KMP,发现这个也是一个用失配函数来匹配自己的过程。在推算f[i+1]的时候,先令j=f[i],这样做直接保证了长度限制,并且如果可以匹配成功一定是最优的。然后在fail函数上匹配,直到成功为止。这个时候j后移一位,判断一下长度限制,如果不符合在fail上面再回跳一次。
主要是用到了fail函数一种理解方式:f[i]表示的是前i个字符最长相同前后缀长度。(这个前后缀是不包括原串的)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int maxn=1000005; 14 const int mo=1000000007; 15 16 int N,f1[maxn],f2[maxn],h[maxn]; 17 char S[maxn]; 18 19 void getfail(char *p) 20 { 21 int m=strlen(p); 22 f1[0]=f1[1]=0; 23 for(int i=1;i<m;i++){ 24 int j=f1[i]; 25 while(j&&p[j]!=p[i]) j=f1[j]; 26 f1[i+1]=p[j]==p[i]?j+1:0; 27 } 28 f2[0]=f2[1]=0; 29 for(int i=1;i<m;i++){ 30 int j=f2[i]; 31 while(j&&(p[j]!=p[i])) j=f1[j]; 32 if(p[j]==p[i]) j++; 33 if(j*2>i+1) j=f1[j]; 34 f2[i+1]=j; 35 } 36 } 37 int main() 38 { 39 scanf("%d\n",&N); 40 for(int i=1;i<=N;i++){ 41 gets(S); getfail(S); 42 int n=strlen(S); 43 h[1]=0; 44 for(int i=2;i<=n;i++) h[i]=h[f1[i]]+(f1[i]?1:0); 45 int ans=1; 46 for(int i=1;i<=n;i++) ans=(1ll*ans*(h[f2[i]]+(f2[i]?1:0)+1))%mo; 47 printf("%d\n",ans); 48 } 49 return 0; 50 }