KMP:c++
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,所以人们称它为克努特——莫里斯——普拉特操做(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽可能减小模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数自己包含了模式串的局部匹配信息。时间复杂度O(m+n)。算法
——百度百科。数组
自我理解:ide
kmp算法最最最最核心的思想,就是在每一次失去匹配的时候,找到最大的可能可以匹配的子段进行匹配。函数
也就是著名的nxt数组。spa
算法流程:code
数组只介绍一个nxt数组。nxt[i]表示,从1~i前缀S中,最长的前缀等于后缀的长度(不能是S)。blog
在每次失去匹配的时候,由于上次已经可以匹配到j了,因此1~j和 i-j~i-1 是相等的。字符串
因此咱们让j=nxt[j]的时候,根据定义,1~nxt[j] = j-nxt[j]+1~j , 又由于:1~j = i-j~i-1 因此 j-nxt[j]+1 ~ j = i-nxt[j] ~ i-1get
这样,咱们退一步到nxt,能够找到失去匹配后,最大的可能再次匹配上的字段长度nxt[j]
代码实现:
void kmp(){ nxt[1]=0; for(int i=2,j=0;i<=l2;i++){ while(j>0&&b[i]!=b[j+1]) j=nxt[j]; if(b[i]==b[j+1]) j++; nxt[i]=j; } }
下面这里还加上了f数组,f[i]表示,在a串中,以第i位结尾的全部子串,和b串的前缀最大匹配的长度。
转移是相似的。
void fin(){ for(int i=1,j=0;i<=l1;i++){ while(j>0&&(j==l2||a[i]!=b[j+1])) j=nxt[j]; if(a[i]==b[j+1]) j++; f[i]=j; if(f[i]==l2){ printf("%d\n",i-l2+1); } } }
这样就能够求出模式串在原始串中出现的位置和个数了。
应用:
1.模式串在主串中出现的次数。见上述代码。
2.求一个串的循环节:
长度为n的字符串的最短循环节是:n-nxt[n],(能够证实:1.能够循环 2.是最短的)
当n%(n-nxt[n])等于0的时候,字符串是一个循环字符串。最长循环次数为:n/(n-nxt[n])
应用例题:
T1:NOI2014 动物园
这个题考察S的前缀中,处理不重叠前缀等于后缀的数量。
能够先求出重叠的nxt[i],num1[]
在处理不重叠的num2[]的时候,跳nxt[j]的时候,跳跃不中止的条件加上一个2*j>i还要跳,由于不能重叠。
跳完后,判断可否加1,若是2*j>i再跳一次。(WA了。。。)
最后,num2[i]=num1[j]+1 注意,这里必定是num1,也就是可重叠的。由于2*j<=i,因此,j中重叠是没有关系的。(WA了。。。)
加一表示1~j和i-j+1~i也是一个。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1000000+10; const int mod=1e9+7; int nxt1[N],nxt2[N],num1[N],num2[N];//1: can 2: can't folded char a[N]; int l; ll ans; void kmp1(){ nxt1[1]=0,num1[1]=0; for(int i=2,j=0;i<=l;i++){ while(j&&a[i]!=a[j+1]) j=nxt1[j]; if(a[i]==a[j+1]) j++; nxt1[i]=j; if(nxt1[i]) num1[i]=num1[j]+1; } } void kmp2(){ nxt2[1]=0;num2[1]=0; for(int i=2,j=0;i<=l;i++){ while(j&&(2*j>i||a[i]!=a[j+1])) j=nxt1[j];//warning!! if(a[i]==a[j+1]) j++; if(2*j>i) j=nxt1[j];//warning!! nxt2[i]=j; if(nxt2[i]) num2[i]=num1[j]+1; } } int main() { int n; scanf("%d",&n); while(n--){ scanf("%s",a+1); ans=1; l=strlen(a+1); kmp1(); kmp2(); for(int i=1;i<=l;i++){ ans=(ans*((ll)1+num2[i]))%mod; } printf("%lld\n",ans); memset(num1,0,sizeof num1); memset(num2,0,sizeof num2); } return 0; }
T2:bzoj4641 基因改造