目的:在字符串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 ",m); else if(len%m==0)printf("%d ",0); else printf("%d ",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 ",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 ",i,i/(i-Next[i])); } printf(" "); } 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 ",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 ",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 ",res); else { ch[res]='