KMP演算法的Next陣列詳解
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) { char[] strKey = ps.toCharArray(); int[] next = new int[strKey.length]; // 初始條件 int j = 0; int k = -1; next[0] = -1; // 根據已知的前j位推測第j+1位 while (j < strKey.length - 1) { if (k == -1 || strKey[j] == strKey[k]) { next[++j] = ++k; } else { k = next[k]; } } 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) { char[] strKey = ps.toCharArray(); int[] next = new int[strKey.length]; // 初始條件 int j = 0; int k = -1; next[0] = -1; // 根據已知的前j位推測第j+1位 while (j < strKey.length - 1) { if (k == -1 || strKey[j] == strKey[k]) { // 如果str[j + 1] == str[k + 1],回退後仍然失配,所以要繼續回退 if (str[j + 1] == str[k + 1]) { k = next[k + 1]; next[++j] = k; } else { k = k + 1; next[++j] = k; } } else { k = next[k]; } } return next; }
好了,自此KMP的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陣列詳解)資料結構KMP演算法陣列
- KMP演算法next陣列的深入理解KMP演算法陣列
- KMP演算法的next、next value陣列的手工計算KMP演算法陣列
- KMP演算法中關於next陣列的探究KMP演算法陣列
- kmp 演算法簡介及 next 陣列推導KMP演算法陣列
- KMP演算法以及優化(程式碼分析以及求解next陣列和nextval陣列)KMP演算法優化陣列
- 關於 KMP next 陣列的應用KMP陣列
- KMP演算法中我對獲取next陣列的理解KMP演算法陣列
- KMP演算法的next、next value陣列程式碼實現及POJ3461KMP演算法陣列
- KMP演算法詳解KMP演算法
- KMP演算法詳解 轉帖KMP演算法
- BZOJ 3670 [Noi2014]動物園 (KMP next陣列應用)KMP陣列
- POJ 2406-Power Strings(重複子串-KMP中的next陣列)KMP陣列
- POJ 2752+KMP+利用next陣列性質求出所有相同的字首和字尾KMP陣列
- Javascript陣列詳解JavaScript陣列
- KMP演算法超詳解與其應用KMP演算法
- 動態規劃之 KMP 演算法詳解動態規劃KMP演算法
- 樹狀陣列詳解陣列
- 字尾陣列詳解陣列
- js陣列方法詳解JS陣列
- PHP陣列的詳細解讀PHP陣列
- 解讀KMP演算法KMP演算法
- 切片底層陣列詳解陣列
- JavaScript遍歷陣列詳解JavaScript陣列
- Objective-C陣列詳解Object陣列
- JavaScript 陣列中的 indexOf 方法詳解JavaScript陣列Index
- python輸入詳解(陣列、矩陣)Python陣列矩陣
- KMP字串模式匹配詳解KMP字串模式
- Java break、continue 詳解與陣列深入解析:單維陣列和多維陣列詳細教程Java陣列
- Numpy陣列的組合與分割詳解陣列
- C++ 獲取陣列大小、多維陣列操作詳解C++陣列
- JavaScript陣列詳解-全網最全JavaScript陣列
- js陣列方法詳解(最新最全)JS陣列
- PostgreSQL 陣列型別使用詳解SQL陣列型別
- js 陣列深度拷貝詳解JS陣列
- 陣列演算法-差分陣列陣列演算法
- PHP 陣列current和next用法分享PHP陣列
- C指標和陣列的關係詳解指標陣列