kmp算法模板
目的:在字符串T中查找字符串P的出现位置,预处理P字符串得到fail数组
时间复杂度:O(|P|+|T|)
next[i]定义为字符s[i]前面最多有多少个连续的字符和字符串s从初始位置开始的字符匹配,i∈[1,n]
循环节问题:
字符串长度为n,若n%(n-Next[n])==0,必有循环节,且循环节长为n-Next[n];
n%(n-Next[n])!=0时可能存在错位、递推周期性相等的情况,造成不完全循环,此时循环周期依然为n-Next[n]。
自用模板:
#include <iostream> #include <string.h> using namespace std; int Next[1110]; void get_Next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j]) {j=Next[j];} Next[i+1]=p[i]==p[j]?j+1:0; } } int kmp(char *T,char *s) { int n=strlen(T),m=strlen(s); get_Next(s); int j=0; for(int i=0;i<n;i++) { while(j&&T[i]!=s[j]) {j=Next[j];} if(s[j]==T[i]) {j++;} if(j==m) { return i-m+1; } } return -1; } int main() { char s[1100],T[1100]; cin>>T>>s; cout<<kmp(T,s)<<endl; //在T中找s return 0; }
修改模板(在字符串T中查找P的出现次数):
int kmp(char *T,char *s) { int n=strlen(T),m=strlen(s),ans=0; get_Next(s); int j=0; for(int i=0;i<n;i++) { while(j&&T[i]!=s[j]) {j=Next[j];} if(s[j]==T[i]) {j++;} if(j==m) { ans++; j=Next[j]; } } return ans; }
循环节长度len-Next[len]
hdu3746,末尾加珠子,变成有循环节的手链,至少加几颗
https://blog.csdn.net/a1097304791/article/details/100538195
#include <bits/stdc++.h> using namespace std; const int maxn=1e5+10; int Next[maxn]; void get_Next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j]) {j=Next[j];} Next[i+1]=p[i]==p[j]?j+1:0; } } int main() { int T; scanf("%d",&T); while(T--) { char s[maxn]; scanf("%s",s); get_Next(s); int len=strlen(s),m=0; for(int i=1;i<=len;i++) { m=max(m,i-Next[i]); } if(Next[len]==0)printf("%d\n",m); else if(len%m==0)printf("%d\n",0); else printf("%d\n",m-len%m); } return 0; }
hdu1358,求字符串的前缀是否为周期串,打印循环节的长度及循环次数
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int Next[maxn]; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int main() { int n,t=1; while(scanf("%d",&n)) { if(n==0)return 0; printf("Test case #%d\n",t++); char s[maxn]; scanf("%s",s); get_next(s); int len=strlen(s),m=0; for(int i=1;i<=len;i++) { if(i%(i-Next[i])==0&&i/(i-Next[i])!=1) printf("%d %d\n",i,i/(i-Next[i])); } printf("\n"); } return 0; }
poj2406,给出一个字符串,问它最多由多少相同的字串组成 ,如abababab由4个ab组成
#include<stdio.h> #include<iostream> #include<string.h> using namespace std; const int maxn=1e6+10; int Next[maxn]; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int main() { char ch[maxn]; while(scanf("%s",ch)) { if(ch[0]=='.'&&strlen(ch)==1)return 0; get_next(ch); int len=strlen(ch),ans,m=len-Next[len]; if(Next[len]==0||len%m!=0)ans=1; else ans=len/m; printf("%d\n",ans); } return 0; }
poj2752,给你一串字符串s找到所有的公共前后缀,即既是前缀又是后缀的子串
#include<iostream> #include<stdio.h> #include<string.h> #include<stack> using namespace std; const int maxn=4e5+10; int Next[maxn]; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int main() { char ch[maxn]; while(scanf("%s",ch)!=EOF) { int len=strlen(ch); get_next(ch); stack<int> s; int tmp=len; while(tmp!=0) { s.push(tmp); tmp=Next[tmp]; } while(!s.empty()) { if(s.size()==1)printf("%d\n",s.top()); else printf("%d ",s.top()); s.pop(); } } return 0; }
hdu2954,kmp&ex-kmp都可做,求s1的前缀和s2的后缀的最长公共长度
kmp做法
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int Next[maxn]; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int main() { char ch[maxn],ch2[maxn]; while(scanf("%s%s",ch,ch2)!=EOF) { int len1=strlen(ch),len2=strlen(ch2); strcat(ch,ch2); get_next(ch); int res=Next[strlen(ch)]; while(res>len1||res>len2)res=Next[res]; if(res==0)printf("%d\n",res); else { ch[res]='\0'; printf("%s ",ch); printf("%d\n",res); } } return 0; }
exkmp做法
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int Next[maxn],extend[maxn]; void get_next(char *s) { int n=strlen(s),i,j,k; for(j=0;1+j<n&&s[j]==s[1+j];j++); Next[1]=j; k=1; for(i=2;i<n;i++) { int len=k+Next[k],L=Next[i-k]; if(L<len-i)Next[i]=L; else { for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++); Next[i]=j; k=i; } } Next[0]=n; } void ex_kmp(char *t,char *s) { int n=strlen(t),m=strlen(s),i,j,k; for(j=0;j<n&&j<m&&t[j]==s[j];j++); extend[0]=j; k=0; for(i=1;i<n;i++) { int len=k+extend[k],L=Next[i-k]; if(L<len-i)extend[i]=L; else { for(j=max(0,len-i);j<m&&i+j<n&&s[j]==t[i+j];j++); extend[i]=j; k=i; } } } int main() { char s[maxn],t[maxn]; while(scanf("%s%s",s,t)!=EOF) { get_next(s); ex_kmp(t,s); int res=0,lent=strlen(t),lens=strlen(s); for(int i=0;i<lent;i++) { if(extend[i]+i==lent) res=max(res,extend[i]); } if(res==0)printf("0\n"); else { s[res]='\0'; printf("%s %d\n",s,res); } } return 0; }
hdu3366,求所有前缀出现的次数之和
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+10; int Next[maxn]; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int main() { int T,mod=10007; scanf("%d",&T); while(T--) { int len,res[maxn]={0},ans=0; char ch[maxn]; scanf("%d",&len); scanf("%s",ch); get_next(ch); for(int i=1;i<=len;i++)res[i]=1; for(int i=len;i>0;i--) { res[Next[i]]=(res[Next[i]]+res[i])%mod; ans=(ans+res[i]%mod)%mod; } printf("%d\n",ans); } return 0; }
hdu4763,求同时出现在串开头、末尾、中间的最长长度,如aabaabaa就是2
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int Next[maxn]; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int main() { int T; scanf("%d",&T); while(T--) { char s[maxn]; scanf("%s",s); get_next(s); map<int,int>mp; int len=strlen(s),ans=0,tmp=len; for(int i=2;i<len;i++) { mp[Next[i]]++; } while(Next[tmp]) { if(mp[Next[tmp]]!=0){ans=Next[tmp];break;} tmp=Next[tmp]; } printf("%d\n",ans); } return 0; }
n个串最长公共子串系列:
hdu1238,找出最长的一个子串,所有串中都出现过该串or反着的该串,暴力可做,n=1可特判,输出串长度
#include<bits/stdc++.h> using namespace std; const int maxn=300; int Next[maxn],n; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int kmp(char *t,char *s) { int n=strlen(t),m=strlen(s); get_next(s); int j=0,maxn=0; for(int i=0;i<n;i++) { while(j&&t[i]!=s[j])j=Next[j],maxn=max(maxn,j); if(s[j]==t[i])j++; maxn=max(j,maxn); if(j==m||j==n)return j; } if(maxn>m||maxn>n)maxn=min(n,m); return maxn; } char ch[4050][250]; char ch2[250]; int check(int tmp) { int res=strlen(ch[0]); for(int i=1;i<n;i++) { int len=strlen(ch[i]),k=0; for(int j=len-1;j>=0;j--) ch2[k++]=ch[i][j]; ch2[k]='\0'; res=min(res,max(kmp(ch[i],ch[0]+tmp),kmp(ch2,ch[0]+tmp))); } return res; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=0;i<n;i++)scanf("%s",ch[i]); int res=0; for(int i=0;i<strlen(ch[0]);i++) { res=max(res,check(i)); } printf("%d\n",res); } return 0; }
hdu2328,字典序最小的最长公共子串,输出该串
#include<bits/stdc++.h> using namespace std; const int maxn=300; int Next[maxn],n; void get_next(char *p) { int m=strlen(p); Next[0]=Next[1]=0; for(int i=1;i<m;i++) { int j=Next[i]; while(j&&p[i]!=p[j])j=Next[j]; Next[i+1]=p[i]==p[j]?j+1:0; } } int kmp(char *t,char *s) { int n=strlen(t),m=strlen(s); get_next(s); int j=0,maxn=0; for(int i=0;i<n;i++) { while(j&&t[i]!=s[j])j=Next[j],maxn=max(maxn,j); if(s[j]==t[i])j++; maxn=max(j,maxn); if(j==m||j==n)return j; } if(maxn>m||maxn>n)maxn=min(n,m); return maxn; } char ch[4050][maxn]; int check(int tmp,int q) { int res=strlen(ch[q]); for(int i=1;i<n;i++) { res=min(res,kmp(ch[i],ch[q]+tmp)); } return res; } int main() { while(scanf("%d",&n)!=EOF) { if(n==0)return 0; int lenmin=0; for(int i=0;i<n;i++) { scanf("%s",ch[i]); if(strlen(ch[i])<strlen(ch[lenmin])) lenmin=i; } char res[maxn]={'\0'},res2[maxn]={'\0'}; int ans=0; for(int i=0;i<strlen(ch[lenmin]);i++) //我怎么又写成了n.... { int tmp=check(i,lenmin); if(tmp>ans) { ans=tmp; strncpy(res,ch[lenmin]+i,tmp);res[tmp]='\0'; } else if(tmp==ans) { strncpy(res2,ch[lenmin]+i,tmp);res2[tmp]='\0'; if(strcmp(res,res2)>0) strcpy(res,res2); } } if(strlen(res)>0)printf("%s\n",res); else printf("IDENTITY LOST\n"); } return 0; }
hdu2328,后缀数组+二分做法
https://www.cnblogs.com/geloutingyu/p/7467440.html
...