關於KMP演算法的next陣列

2021-09-12 19:17:20 字數 2928 閱讀 3154

kmp的next陣列求法是很不容易搞清楚的一部分,也是最重要的一部分。我這篇文章就以我自己的感悟來慢慢推導一下吧!保證你看完過後是知其然,也知其所以然。

如果你還不知道kmp是什麼,請先閱讀上面的鏈結,先搞懂kmp是要幹什麼。

下面我們就來說說kmp的next陣列求法。

kmp的next陣列簡單來說,假設有兩個字串,乙個是待匹配的字串strtext,乙個是要查詢的關鍵字strkey。現在我們要在strtext中去查詢是否包含strkey,用i來表示strtext遍歷到了哪個字元,用j來表示strkey匹配到了哪個字元。

如果是暴力的查詢方法,當strtext[i]和strkey[j]匹配失敗的時候,i和j都要回退,然後從i-j的下乙個字元開始重新匹配。

而kmp就是保證i永遠不回退,只回退j來使得匹配效率有所提公升。它用的方法就是利用strkey在失配的j為之前的成功匹配的子串的特徵來尋找j應該回退的位置。而這個子串的特徵就是前字尾的相同程度。

所以next陣列其實就是查詢strkey中每一位前面的子串的前字尾有多少位匹配,從而決定j失配時應該回退到哪個位置。

我知道上面那段廢話很難懂,下面我們看乙個彩圖:

這個圖畫的就是strkey這個要查詢的關鍵字字串。假設我們有乙個空的next陣列,我們的工作就是要在這個next陣列中填值。

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

這裡我們借鑑數學歸納法的三個步驟(或者說是動態規劃?):

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]即是找strkey從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] == 綠色色塊所在的索引,我們先讓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;

public static int getnext(string ps)

else

}return next;

}

現在再看這段**應該沒有任何問題了吧。

優化:細心的朋友應該發現了,上面有這樣一句話:

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

可是我們知道,第j+1位是失配了的,如果我們回退j後,發現新的j(也就是此時的++k那位)跟回退之前的j也相等的話,必然也是失配。所以還得繼續往前回退。

public static int getnext(string ps)

else

}else

}return next;

}

下面說說面試的時候,給乙個字串,要你寫出它的next陣列,應該怎麼寫:

①:先對每一位左邊的子串求出最大前字尾串的長度,作為初始的next陣列

②:因為第一位失配時需要移動i,因此賦值為-1

③:p[3] == a, next[3] == 0, p[0] == a;  所以p[3] == p[0], (移動過去後還是失配,需要繼續移動),優化next[3]為next[0],即-1

④:同理優化next[10]為next[0],即-1

⑤:同理優化p[14],p[15],p[16]

**:

關於KMP的next陣列

明明寫的和課本一樣,但是在pta就是不正確,參考了別人的答案,發現有乙個地方不一樣記錄一下!include include include int next1 1000 next1 j k,表示當t i p j 時,j指標的下乙個位置。求字串在主串中第一次出現的位置 void get next1 c...

KMP演算法 next陣列

通過上文完全可以對kmp演算法的原理有個清晰的了解,那麼下一步就是程式設計實現了,其中最重要的就是如何根據待匹配的模版字串求出對應每一位的最大相同前字尾的長度。我先給出我的 1 void makenext const char p,int next 214 next q k 15 16 現在我著重講...

KMP演算法 NEXT陣列

kmp和next陣列基本上是一起用的,有了next陣列,才有kmp演算法,講道理來說這兩個都是基於最大前字尾和,也就是說需要用到kmp的時候必須先把next陣列先求出來,next陣列就是由所匹配的word的每個子串的前字尾和最大匹配得到的,說實話next陣列的演算法給優化得已經很無解了,以至於至今我...