KMP 模式串匹配 失去匹配的瞬間你還有什麼

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 基因改造

相關文章
相關標籤/搜索