几道用到KMP的DP题:
hdu 5763 hdu 3689 hdu 3336 codeforces 494B codevs 3945
关于KMP的nx数组:
如果在本文中看见了nx[]指的是所谓“成功指针”,或者getnx()函数跟本人之前的板子写的不一样......
都是因为求nx数组有两种方法!!!
详情请阅本人的另一篇文章:关于KMP算法的重大发现
好了我们进入正题~
一道一道来~
hdu 5763 Another Meaning
题意及样例:原题链接
设第一个串为A,长为n;第二个串为B,长为L
从1到n计算1~k能代表的意思的数量f[k]
如果A[k-L+1,k]==B
则f[k]=f[k-L]+f[k-1]
否则f[k]=f[k-1]
判断A[k-L+1,k]是否与B匹配就要靠KMP了
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mod 1000000007 5 using namespace std; 6 7 int t,al,bl; 8 char a[100005]; 9 char b[100005]; 10 int nx[100005]; 11 int fl[100005]; 12 int f[100005]; 13 14 void getnx() 15 { 16 nx[1]=0; 17 for(int i=2,j=1;i<=bl;) 18 { 19 nx[i]=j; 20 while(j&&b[j]!=b[i])j=nx[j]; 21 j++,i++; 22 } 23 } 24 25 void kmp() 26 { 27 for(int i=1,j=1;i<=al;) 28 { 29 while(j&&b[j]!=a[i])j=nx[j]; 30 if(j==bl) 31 { 32 fl[i]=1; 33 j=nx[j]; 34 } 35 else j++,i++; 36 } 37 } 38 39 int main() 40 { 41 scanf("%d",&t); 42 for(int cs=1;cs<=t;cs++) 43 { 44 memset(a,0,sizeof(a)); 45 memset(b,0,sizeof(b)); 46 memset(fl,0,sizeof(fl)); 47 scanf("%s",a+1); 48 scanf("%s",b+1); 49 al=strlen(a+1); 50 bl=strlen(b+1); 51 getnx(); 52 kmp(); 53 f[0]=1; 54 for(int i=1;i<=al;i++) 55 { 56 if(fl[i])f[i]=(f[i-1]+f[i-bl])%mod; 57 else f[i]=f[i-1]; 58 } 59 printf("Case #%d: %d ",cs,f[al]); 60 } 61 return 0; 62 }
hdu 3689 Infinite monkey theorem
概率DP,不一定一出错就要重头开始打。用nx[ ]数组转移,避免字符的浪费。
这里的nx[ ]数组指的是成功指针......(玄学)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int n,m,len; 7 double p[30]; 8 char s[15]; 9 char key[30]; 10 int nx[15]; 11 double f[1005][15]; 12 13 void getnx() 14 { 15 nx[1]=0; 16 for(int i=2,j=0;i<=len;i++) 17 { 18 while(j&&s[i]!=s[j+1])j=nx[j]; 19 if(s[j+1]==s[i])j++; 20 nx[i]=j; 21 } 22 } 23 24 int main() 25 { 26 while(true) 27 { 28 memset(nx,0,sizeof(nx)); 29 for(int i=1;i<=26;i++)p[i]=0.00; 30 for(int i=0;i<=1001;i++) 31 { 32 for(int j=0;j<=11;j++) 33 { 34 f[i][j]=0.00; 35 } 36 } 37 scanf("%d%d",&n,&m); 38 if(!n)return 0; 39 for(int i=1;i<=n;i++) 40 { 41 char c[5]; 42 scanf("%s",c+1); 43 key[i]=c[1]; 44 scanf("%lf",&p[i]); 45 } 46 scanf("%s",s+1); 47 len=strlen(s+1); 48 getnx(); 49 f[0][0]=1.00; 50 for(int i=1;i<=m;i++) 51 { 52 for(int j=0;j<len;j++) 53 { 54 for(int k=1;k<=n;k++) 55 { 56 int ps=j; 57 while(ps&&s[ps+1]!=key[k])ps=nx[ps]; 58 if(s[ps+1]==key[k])ps++; 59 f[i][ps]+=f[i-1][j]*p[k]; 60 } 61 } 62 } 63 double ans=0.00; 64 for(int i=1;i<=m;i++)ans+=f[i][len]; 65 printf("%.2lf%% ",ans*100.00); 66 } 67 }
hdu 3336 Count the string
利用KMP中nx[ ]数组的性质。
上代码。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 5 int t,n,len,f[200005],ans; 6 char s[200005]; 7 int nx[200005]; 8 9 void getnx() 10 { 11 for(int i=1;i<n;i++) 12 { 13 int j=nx[i]; 14 while(j&&s[i]!=s[j]) j=nx[j]; 15 nx[i+1]= s[i]==s[j] ? j+1:0; 16 } 17 } 18 int main() 19 { 20 scanf("%d",&t); 21 while(t--) 22 { 23 scanf("%d",&n); 24 scanf("%s",s); 25 len=strlen(s); 26 getnx(); 27 memset(f,0,sizeof(f)); 28 ans=0; 29 for(int i=1;i<=n;i++) 30 { 31 f[i]=f[nx[i]]+1; 32 ans+=f[i]; 33 f[i]%=10007; 34 ans%=10007; 35 } 36 printf("%d ",ans); 37 } 38 }
CodeForces 494B Obsessive String
比较复杂......
记录f[ ],f[ ]的前缀和s[ ],还有s[ ]的前缀和ss[ ] 。
KMP用来匹配字符串。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mod 1000000007 5 using namespace std; 6 7 char a[100005],b[100005]; 8 int al,bl; 9 int nx[100005]; 10 int fl[100005]; 11 int f[100005],s[100005],ss[100005]; 12 13 void getnx() 14 { 15 nx[1]=0; 16 for(int i=2,j=1;i<=bl;) 17 { 18 nx[i]=j; 19 while(j&&b[j]!=b[i])j=nx[j]; 20 i++,j++; 21 } 22 } 23 24 void kmp() 25 { 26 for(int i=1,j=1;i<=al;) 27 { 28 while(j&&a[i]!=b[j])j=nx[j]; 29 if(j==bl)fl[i]=1,j=nx[j]; 30 else i++,j++; 31 } 32 } 33 34 int main() 35 { 36 scanf("%s",a+1); 37 al=strlen(a+1); 38 scanf("%s",b+1); 39 bl=strlen(b+1); 40 getnx(); 41 kmp(); 42 for(int i=1;i<=al;i++) 43 { 44 if(fl[i])f[i]=(i-bl+1+ss[i-bl])%mod; 45 else f[i]=f[i-1]; 46 s[i]=(s[i-1]+f[i])%mod; 47 ss[i]=(ss[i-1]+s[i])%mod; 48 } 49 int ans=0; 50 for(int i=1;i<=al;i++)ans=(ans+f[i])%mod; 51 printf("%d",ans); 52 }
CODEVS 3945 完美拓印
当成字符串匹配......
坑挺多的,各种方向都得匹配一次求出答案。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int a[1000005],b[1000005],s1[1000005],s2[1000005],nx[1000005]; 7 int n,m,ans; 8 9 void getnx() 10 { 11 nx[0]=nx[1]=0; 12 for(int i=1;i<n;i++) 13 { 14 int j=nx[i]; 15 while(j&&s1[i]!=s1[j])j=nx[j]; 16 nx[i+1]=(s1[i]==s1[j]?j+1:0); 17 } 18 } 19 20 int kmp() 21 { 22 getnx(); 23 int j=0,cnt=0; 24 for(int i=0;i<m;i++) 25 { 26 while(j&&s2[i]!=s1[j])j=nx[j]; 27 if(s2[i]==s1[j])j++; 28 if(j==n)cnt++,j=nx[j]; 29 } 30 return cnt; 31 } 32 33 int main() 34 { 35 scanf("%d%d",&n,&m); 36 for(int i=0;i<n;i++)scanf("%d",&a[i]); 37 for(int i=0;i<m;i++)scanf("%d",&b[i]); 38 if(n==1) 39 { 40 printf("%d ",m<<2); 41 return 0; 42 } 43 n--; m--; 44 for(int i=0;i<n;i++)s1[i]=a[i+1]-a[i]; 45 for(int i=0;i<m;i++)s2[i]=b[i+1]-b[i]; 46 ans+=kmp(); 47 for(int i=0;i<n;i++)s1[i]=a[n-i]-a[n-i-1]; 48 ans+=kmp(); 49 for(int i=0;i<n;i++)s1[i]=0; 50 ans+=2*kmp(); 51 printf("%d",ans); 52 return 0; 53 }
结束了。
最后顺便说,有些字符串匹配的题可以用hash做。
这样可以偷懒,不用写KMP
好吧还是乖乖背KMP板子吧。
溜了。