KMP算法學習總結

今天看了不少關於KMP算法的東東 讀了好幾篇別人寫的博客 零零碎碎的算是大體明白是個怎麼回事兒了 

現在好好把別人的東西 整理整理 部分是轉載的哦!

KMP算法核心:

1、KMP算法藉助於一個輔助數組next來確定當匹配過程中出現不等時,模式P右移的位置和開始比較的位置。

2、next[i]的取值只與模式P本身的前i+1項有關,而與目標T無關。

3、匹配過程中遇到Pi不等於Tj時,若next[i]>=0,則應將P右移i-next[i]位個字符,用P中的第next[i]個字符與Tj 進行比較

(這句話也可以這樣理解 當出現不匹配時(不匹配位置爲i) 計算 i=next[i], 將模式P i處的值與不匹配的原竄對齊,繼續比較)

4、若:next[i]= -1,P中的任何字符都不必再與Tj比較,而應將P右移i+1個字符,從P0和Tj+1從新開始下一輪比較


next的計算方法如下(轉的,我覺得講的挺好 而且考慮的挺全面的)

書上有的東西我就不說了,那些東西網上一搜一大片,我主要說一下我理解的由前綴函數生成的next數組的含義,先貼出求next數組的方法。

void GetNext(char* t, int* next)
{
    int i, j, len;
    i = 0;
    j = -1;
    next[0] = -1;
    while(t[i] != '\0')
    {
        if (j == -1 || t[i] == t[j])
        {
            i++;
            j++;
            next[i] = j;
        }
        else
        {
            j = next[j];
        }
    }
}


當一個字符串以0爲起始下標時,next[i]可以描述爲"不爲自身的最大首尾重複子串長度"。
也就是說,從模式串T[0...i-1]的第一個字符開始截取一段長度爲m(m < i-1)子串,再截取模式串T[0...i-1]的最後m個字符作爲子串,如果這兩個子串相等,則該串就是一個首尾重複子串。我們的目的就是要找出這個最大的m值。

例如:

KMP算法

若 i = 4 ,則 i - 1 = 3 , m = next[4] = 2

從T[0...3]截取長度爲2的子串,爲"ab"

從T[0..3]截取最後2個字符,爲"ab"

此時2個子串相等,則說明 next[4] = 2 成立,也可證明 m = 2 爲最大的m值。

本來一開始我是沒有加"不爲自身"這個限制條件的,可是後來我發現一種情況:

KMP算法

若 i = 4 ,則 i - 1 = 3 , m = next[4] = 3

從T[0...3]截取長度爲3的子串,爲"aaa"

從T[0..3]截取最後3個字符,爲"aaa"

此時2個子串相等,則說明 next[4] = 3 成立。

但是我發現如果next[4] = 4:  //這裏容易犯錯!!!

從T[0...3]截取長度爲4的子串,爲"aaaa"

從T[0..3]截取最後4個字符,爲"aaaa"

此時2個子串也是相等的,那麼是不是說明 next[4] 應該等於4呢?

仔細觀察後發現,如果 next[4] = 4 ,那麼T[0...3]的前4個字符和後4個字符是重合的,並且重複子串和T[0...3]也是相等的。看過教材後發現教材中給出的前綴函數定義有一句爲:next[j] = max{k | 0 < k < j 且 'p[0]...p[k-1]' = 'p[j-k+1]...p[j-1]'},應該不包含子串爲本身的情況...

這樣再做PKU 2406 和 PKU 1961 的時候就很簡單了,用 length - next[length] 求出"不爲自身的最大首尾重複子串長度",此時需要多求一位next[length]值,若最大重複子串的長度是length的非1整數倍,則證明字符串具有周期重複性質。

PKU 2752 是求 前綴 == 後綴 的長度,也就是首尾重複子串長度,利用next數組記錄的"不爲自身的最大首尾重複子串長度"可以馬上得到結果。


舉例說明:

目標串:T: abababcb

模式串:   P:ababc

計算P的next 函數

i               0        1         2         3         4         

s              a         b        a         b          c

next[i]      -1        0        0         1         2

開始匹配

第一步: 不匹配的爲c index爲4  next[4]=2

T          a      b      a      b      a      b      c      b

P          a      b      a      b      

第二步:將index爲next[4]=2的數字(綠色標示)與 T 中標紅的a對齊

T          a      b      a      b      a      b      c      b

P                          a      b      a      b      c

這裏的例子舉得有點簡單 一下子就對其了 複雜的道理一樣!代碼如下所示:

int kmp(char T[],char P[],int next[])
{
	int i=0,j=0,len1=strlen(T),len2=strlen(P);
    while((i<len1)&&(j<len2))
    {
        if(j==-1||T[i]==P[j]) 
		{
			j++;
			i++;
		}
        else 
			j=next[j];
    }
    if(j==len2) return i-len2;
    else return -1;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章