kmp常见用法
1.字符串匹配
例题:「POJ3461」Oulipo 「HDU2087」剪花布条 「USACO2015FEB」Censoring (Silver)
#include<bits/stdc++.h> using namespace std; const int N = 1e6+5; char s[N],t[N]; int lens,lent; int stk[N],top; int kmp[N],f[N]; int main(){ scanf("%s%s",s+1,t+1); lens=strlen(s+1); lent=strlen(t+1); kmp[1]=0; for(int i=2,j=0;i<=lent;i++){ while(j&&t[i]!=t[j+1])j=kmp[j]; if(t[i]==t[j+1])j++; kmp[i]=j; } for(int i=1,j=0;i<=lens;i++){ while(j&&s[i]!=t[j+1])j=kmp[j]; if(s[i]==t[j+1])j++; f[i]=j; stk[++top]=i; if(j==lent){ top-=lent; j=f[stk[top]]; } } for(int i=1;i<=top;i++) printf("%c",s[stk[i]]); return 0; }
#include<bits/stdc++.h> using namespace std; const int N = 1005; char a[N],b[N]; int nex[N]; int main(){ while(1){ cin>>a+1>>b+1;; if(a[1]=='#') break; memset(nex,0,sizeof nex); int lena=strlen(a+1),lenb=strlen(b+1),ans=0; for(int i=2,j=0;i<=lenb;i++){ while(j>0&&b[i]!=b[j+1])j=nex[j]; if(b[i]==b[j+1])j++; nex[i]=j; } for(int i=1,j=0;i<=lena;i++){ while(j>0&&(j==lenb||a[i]!=b[j+1]))j=nex[j]; if(a[i]==b[j+1])j++; if(j==lenb){ ans++; i=i+lenb-1; } } printf("%d\n",ans); } return 0; }
2.最小循环节
例题:「POJ2406」Power Strings 「POJ1961」Period 「BalticOI2009」Radio Transmission
#include<bits/stdc++.h> using namespace std; const int N = 1e6+6; char a[N]; int kmp[N],len; void calc_kmp(){ kmp[1]=0; for(int i=2,j=0;i<=len;i++){ while(j>0&&a[i]!=a[j+1])j=kmp[j]; if(a[i]==a[j+1])j++; kmp[i]=j; } } int main(){ while(1){ scanf("%s",a+1); if(a[1]=='.')return 0; len=strlen(a+1); calc_kmp(); if(len%(len-kmp[len])==0&&len/(len-kmp[len])>1) printf("%d\n",len/(len-kmp[len])); else printf("1\n"); } }
#include<bits/stdc++.h> using namespace std; const int N = 1e6+6; int n,t,kmp[N]; char a[N]; void calc_kmp(){ kmp[1]=0; for(int i=2,j=0;i<=n;i++){ while(j>0&&a[i]!=a[j+1])j=kmp[j]; if(a[i]==a[j+1])j++; kmp[i]=j; } } int main(){ while(scanf("%d",&n)&&n){ scanf("%s",a+1); calc_kmp(); printf("Test case #%d\n",++t); for(int i=2;i<=n;i++) if(i%(i-kmp[i])==0&&i/(i-kmp[i])>1) printf("%d %d\n",i,i/(i-kmp[i])); printf("\n"); } return 0; }
#include<bits/stdc++.h> #define inf 2000000000 #define ll long long #define mod 1000000007 using namespace std; int n; char a[1000005]; int kmp[1000005]; int main(){ scanf("%d%s",&n,a+1); int j=0; for(int i=2;i<=n;i++){ while(a[j+1]!=a[i]&&j)j=kmp[j]; if(a[j+1]==a[i])j++; kmp[i]=j; } printf("%d",n-kmp[n]); return 0; }
3.既是前缀又是后缀的子串
例题:「POJ2752」Seek the Name, Seek the Fame
#include<bits/stdc++.h> using namespace std; const int N = 4e5+5; char s[N]; int n,kmp[N],ans[N]; void getkmp(){ int j=-1,i=0; kmp[0]=-1; while(i!=n){ if(j==-1||s[i]==s[j])kmp[++i]=++j; else j=kmp[j]; } } int main(){ while(scanf("%s",s)!=EOF){ n=strlen(s); getkmp(); int cnt=0; for(int i=n;i!=-1;i=kmp[i]) ans[++cnt]=i; for(int i=cnt-1;i>=1;i--) printf("%d ",ans[i]); printf("\n"); } return 0; }
4.求不相交前后缀匹配数量
例题:「NOI2014」动物园
#include<bits/stdc++.h> using namespace std; const int mod = 1e9+7; const int N = 1e6+5; int n,kmp[N],num[N]; char s[N]; int main(){ scanf("%d",&n); while(n--){ scanf("%s",s+1); int len=strlen(s+1),ans=1; kmp[1]=0;num[1]=1; for(int i=2,j=0;i<=len;i++){ while(j>0&&s[i]!=s[j+1])j=kmp[j]; if(s[i]==s[j+1])j++; kmp[i]=j; num[i]=num[j]+1; } for(int i=1,j=0;i<=len;i++){ while(j>0&&s[i]!=s[j+1])j=kmp[j]; if(s[i]==s[j+1])j++; while(j>i/2)j=kmp[j]; //printf("%d ",num[j]); ans=(1ll*ans*(num[j]+1))%mod; } printf("%d\n",ans); } return 0; }
5.求字符串‘周期’
例题:[POI2006]OKR-Periods of Words
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1000010; char s[N]; int n,fail[N]; int main(){ scanf("%d%s",&n,s+1); ll ans=0;fail[1]=0; for(int i=2,j=0;i<=n;i++){ while(j&&s[i]!=s[j+1])j=fail[j]; if(s[i]==s[j+1])j++; fail[i]=j; } for(int i=1;i<=n;i++){ int j=i; while(fail[j])j=fail[j]; if(fail[i])fail[i]=j; ans+=i-j; } printf("%lld",ans); return 0; }
6.后缀的前缀 和 字符串的前缀 的 最大公共
例题:【模板】扩展KMP(Z函数) [CF126B] Password
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N =2e7+2; char a[N],b[N]; ll z[N],p[N]; ll lena,lenb,ans; int getsame(){ int cnt=0,num=max(lena,lenb); for(int i=1;i<=num;i++){ if(a[i]==b[i])cnt++; else return cnt; } } void getz(){ z[1]=lenb; int l=0,r=0; for(int i=2;i<=lenb;i++){ if(i<=r)z[i]=min(z[i-l+1],(ll)r-i+1); while(z[i]+i<=lenb&&b[z[i]+i]==b[z[i]+1])z[i]++; if(i+z[i]-1>r)r=i+z[i]-1,l=i; } } void getp(){ p[1]=getsame(); int l=0,r=0; for(int i=1;i<=lena;i++){ if(i<=r)p[i]=min(z[i-l+1],(ll)r-i+1); while(p[i]+i<=lena&&a[p[i]+i]==b[p[i]+1])p[i]++; if(i+p[i]-1>r)r=i+p[i]-1,l=i; } } int main(){ scanf("%s%s",a+1,b+1); lena=strlen(a+1),lenb=strlen(b+1); getz(); for(int i=1;i<=lenb;i++)ans^=(i*(z[i]+1)); printf("%lld\n",ans); ans=0;getp(); for(int i=1;i<=lena;i++)ans^=(i*(p[i]+1)); printf("%lld\n",ans); return 0; }
7.前缀出现次数
例题:[CF432D] Prefixes and Suffixes
#include<bits/stdc++.h> using namespace std; const int N = 1e5+5; char s[N]; int kmp[N],num[N],len,tot; vector<pair<int,int> >ans; int main(){ scanf("%s",s+1); len=strlen(s+1); kmp[1]=0; for(int i=2,j=0;i<=len;i++){ while(j&&s[i]!=s[j+1])j=kmp[j]; if(s[i]==s[j+1])j++; kmp[i]=j; } for(int i=1;i<=len;i++)num[i]++; for(int i=len;i>=1;i--)num[kmp[i]]+=num[i]; for(int k=len;k;k=kmp[k]){ tot++; ans.push_back(make_pair(k,num[k])); } printf("%d\n",tot); for(int i=(int)ans.size()-1;i>=0;i--) printf("%d %d\n",ans[i].first,ans[i].second); return 0; }