kmp演算法講解

2021-07-10 01:39:35 字數 2615 閱讀 7559

kmp演算法本身,解決的是判斷模板字串t,是否是字串s的子串的問題。

當只需判斷子串首次出現的位置,或是否包含子串。可以用庫函式strstr(s,t )代替,判斷t是否為s的子串來代替。該函式的返回值是t首次出現的位置,如果t不是s的子串則返回null。該函式的複雜度與kmp類似。

kmp演算法主要分為兩個部分第一部分是求解nexta陣列(注意此處不能寫next因為肯能會和c++的庫變數重名,導致編譯錯誤),第二部分利用nexta陣列進行匹配。

先說為什麼要求nexta陣列。樸素的匹配方法。當發現字串不匹配時,就要重頭開始重新匹配,造成很高的複雜度(m*n)。如下面三組所示(圖一圖二圖三圖四,來自

kmp演算法對模式串進行處理,得出當匹配到不相等的地方時,指向模式串的指標重新指向的位置(樸素法的指標指向的位置是0)。且指向母串的指標始終是不會向左移動的,及只向右移動。當發現不匹配時利用nexta陣列產生的效果。如圖四所示。只有j改變了i不動,再次判斷。使得複雜度是線性的。

那麼nexta陣列的含義是什麼呢?

設進行處理的字串為s。nexta[i] = k.k是使得s[0...k-1] == s[i - k,i - 1]成立的最大值。

也就是求s的字首,和以i-1結尾的字尾的最大值。

這種字尾等於字首的模式保證移動是合適的

那麼如何求解nexta陣列呢?

此處借鑑「北京小王子」的方法

下面我們看乙個彩圖:

下面我們用數學歸納法來解決這個填值的問題。

這裡我們借鑑數學歸納法的三個步驟:

1、初始狀態

2、假設第j位以及第j位之前的我們都填完了

3、推論第j+1位該怎麼填

初始狀態我們稍後再說,我們這裡直接假設第j位以及第j位之前的我們都填完了。也就是說,從上圖來看,我們有如下已知條件:

next[j] == k;

next[k] == 綠色色塊所在的索引;

next[綠色色塊所在的索引] == 黃色色塊所在的索引;

這裡要做乙個說明:圖上的色塊大小是一樣的(沒騙我?好吧,請忽略色塊大小,色塊只是代表陣列中的一位)。

1.由"next[j] == k;"這個條件,我們可以得到a1子串 == a2子串(根據next陣列的定義,前字尾那個)。

2.由"next[k] == 綠色色塊所在的索引;"這個條件,我們可以得到b1子串 == b2子串。

3.由"next[綠色色塊所在的索引] == 黃色色塊所在的索引;"這個條件,我們可以得到c1子串 == c2子串。

4.由1和2(a1 == a2,b1 == b2)可以得到b1 == b2 == b3。

5.由2和3(b1 == b2, c1 == c2)可以得到c1 == c2 == c3。

6.b2 == b3可以得到c3 == c4 == c1 == c2

上面這個就是很簡單的幾何數學,仔細看看都能看懂的。我這裡用相同顏色的線段表示完全相同的子陣列,方便觀察。

接下來,我們開始用上面得到的條件來推導如果第j+1位失配時,我們應該填寫next[j+1]為多少?

next[j+1]即是找str從0到j這個子串的最大前字尾:

我們已知a1 == a2,那麼a1和a2分別往後增加乙個字元後是否還相等呢?我們得分情況討論:

(1)如果str[k] == str[j],很明顯,我們的next[j+1]就直接等於k+1。

用**來寫就是next[++j] = ++k;

(2)如果str[k] != str[j],那麼我們只能從已知的,除了a1,a2之外,最長的b1,b3這個前字尾來做文章了。(為什麼b1,b3是最長的?假設它們不是最長的,還有更長的next[k]就等於更長的了)

那麼b1和b3分別往後增加乙個字元後是否還相等呢?

由於next[k] == 綠色色塊所在的索引,我們先讓k = next[k],把k挪到綠色色塊的位置。如果還不相等就一直往前挪。挪到開頭會做特殊處理。

由於j+1位之前的next陣列我們都是假設已經求出來了的,因此,上面這個遞迴總會結束,從而得到next[j+1]的值。

我們唯一欠缺的就是初始條件了:

next[0] = -1,  k = -1, j = 0

另外有個特殊情況是k為-1時,不能繼續遞迴了,此時next[j+1]應該等於0,即把j回退到首位。

即 next[j+1] = 0; 也可以寫成next[++j] = ++k

獲得nexta的**如下

//定義nexta大小與s相同

void getnexta(char s)

else}}

下面談談

第二部分利用nexta陣列進行匹配。

同樣設兩個指標i,j初始值都為零。假設j為模式串t的指標,i為母串s的指標。當s[i] ==t[j]時,顯然i ++,j++.當j= -1時j要重新等於零,i也要向右移一位(因為這個位置已經與模式串全部試過了)除了這兩種情況之外,剩餘的狀況及兩個字串不匹配且並未

挪到開頭之前,則j = nexta[j].

以下是kmp**

int kmp(char s,char t)//t模式串,s母串.此種為返回首次匹配的位置,不能匹配則返回-1.

else

if(j == m)//根據題目要求改變

}return -1;

}

KMP演算法講解

第一次寫部落格有點小激動,今天要講一下kmp演算法,這個查詢演算法比較難理解,我也是想了很久才想明白,我們在str2中的pos位置開始查詢str1,當找到的時候返回str2中的下標,沒有找到的時候返回 1 首先我們來參考一下我們最初寫的查詢字串的函式,定義i 0為str2的下標,j 0為str1的下...

擴充套件kmp演算法講解

參考文章 一.擴充套件kmp得到的是什麼 字串a,b。b是模式串 子串 求解a i lena 1 與b的最長公共字首,記錄在陣列ex i 中 二.演算法實現 分為兩步,第一步是對b模式串進行處理也是求b i,blen 1 與b的最長公共字首,用next陣列儲存,第二部是將a與b進行匹配,其實他們的方...

KMP演算法入門講解

字串匹配問題。假設文字是乙個長度為 n 的字串 t 模板是乙個長度為 m 的字串 p 且 m leq n 需要求出模板在文字中的所有匹配點 i 即滿足 t i p 0 t i 1 p 1 t m 1 p m 1 的非負整數 i 注意字串下標從0開始 如圖所示,p 在 t 中有且只有乙個匹配點,即位置...