【黑馬計劃-1】KMP及擴展KMP

KMP

核心

玄學 fail 數組
fail 數組的含義即是某段字符串的最長公共前後綴。
具體來講,設 T[1j1]=T[iji1](ji) ,那麼 fail[i]j 的最大值。

那麼這個 fail 有什麼用呢?見下圖。
發生失配時,當前指針從 $i$ 跳到 $fail[i]$
匹配時,設當前匹配到 S 的第 i 位, T 的第 j 位,即 S[ij+1i1]T[1j1] 已成功匹配。當 j 指針這一位發生失配,意味着 S[i]!=T[j] ,這時根據 fail 數組的定義,由於 T[1fail[j]1]=T[jfail[j]j1] ,因此若將 j 指針指向 fail[j] ,我們可以直接跳過對 T[1fail[j]1] 的匹配。

複雜度證明

n 爲串 S 的長度, m 爲串 T 的長度。
i 指針全程只增,這裏的複雜度爲 O(n)
j 指針全程只有兩種跳法: jj+1jfail[j]
對於 jj+1 全程最多跳 n 次。
對於 jfail[j]
i 保持不變的情況下, j 跳至下界的極限次數一定不超過 j (根據 fail 的定義)。因此設 f[j] 表示 j 這個位置發生失配跳至下界的上限次數, g[j] 爲跳完 f[j] 次之後 j 的位置。而 j 每跳一次, g[j] 一定減小至少 1f[j] 隨之減小至少 1 ,從而最終跳的次數上界爲 U=max{f[j]} 。由 f 的定義我們知道, max{f[x]}=n ,故最終 j 跳的次數一定不超過 n
又由於要單獨對串 T 單獨求一次 fail ,複雜度證明同上,爲 O(m)
綜上,由於 i 全程迭代 n 次, j 全程迭代不超過 n 次,故時間複雜度爲 O(n+m)

擴展KMP

“擴展”

引入 ext 數組, ext[i] 表示 S[in]T[1m] 的最長公共前綴( n 爲串 S 的長度, m 爲串 T 的長度)。
考慮如何求 ext

引入輔助工具

設當前需要計算 ext[i] 的值, pext[j] 最大時 j 的值 (1j<i)
就有 S[pp+ext[p]1]=T[1ext[p]]
於是 S[ip]=T[ext[p]p+iext[p]]
這時求 ext 就有兩種情況:
1、 i+fail[i]<p+ext[p]
由於 S[ip]=T[ext[p]p+iext[p]]
又由 fail 的定義知 S[ii+fail[ip+1]1]=T[1fail[ip+1]]fail[ip+1] 爲最大匹配長度,故 ext[i]=fail[ip+1]
 $i+fail[i]<p+ext[p]$ ,紅色兩段完全相同

2、 i+fail[i]p+ext[p]
此處求法與 fail 求法幾乎一樣,可參考 fail 的求值。

複雜度證明:對於情況1,單次複雜度爲 O(1) ;對於情況2,總複雜度與KMP算法中一致,爲 O(n+m)

代碼實現

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int LENGTH=1000000;
char S[LENGTH+2],T[LENGTH+2];

namespace KMP{
    int fail[LENGTH+2];
    int cnt[LENGTH+2],ext[LENGTH+2];
    void get_fail(char *t){
        int len=strlen(t+1);
        for(int i=2,j=0;i<=len;++i){
            while(j&&t[i]!=t[j+1])j=fail[j];
            if(t[i]==t[j+1])fail[i]=++j;
        }
    }
    void KMP(char *s,char *t){
        get_fail(t);
        int s_len=strlen(s+1),t_len=strlen(t+1);
        for(int i=1,j=0;i<=s_len;++i){
            while(j&&s[i]!=t[j+1])j=fail[j];
            if(s[i]==t[j+1]){
                cnt[i]=++j;
                if(j==t_len)j=fail[j];
            }
        }
    }
    void ex_KMP(char *s,char *t){
        get_fail(t);
        int s_len=strlen(s+1),t_len=strlen(t+1);
        int p=1;
        while(ext[1]<t_len&&s[ext[1]+1]==t[ext[1]+1])++ext[1];
        for(int i=2;i<=s_len;++i){
            if(i+fail[i-p+1]<p+ext[p])ext[i]=fail[i-p+1];
            else{
                int j=ext[p]+p-i;
                if(j<0)j=0;
                while(i+j<=s_len&&j<t_len&&s[i+j]==t[j+1])++j;
                ext[i]=j;
                p=i;
            }
        }
    }
}

int main(){
    scanf("%s%s",S+1,T+1);
    KMP::KMP(S,T);
    KMP::ex_KMP(S,T);
    int s_len=strlen(S+1),t_len=strlen(T+1);
    printf("Array of fail:\n\t");
    for(int i=1;i<=t_len;++i)printf("%d ",KMP::fail[i]);
    printf("\nMatching position:\n\t");
    for(int i=1;i<=s_len;++i)if(KMP::cnt[i]==t_len)printf("%d ",i-t_len+1);
    printf("\nArray of extend:\n\t");
    for(int i=1;i<=s_len;++i)printf("%d ",KMP::ext[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章