KMP算法快速理解

一、最長公共子綴

公共子綴分爲前綴和後綴
前綴:總是包含第一個字符的子串(不包括父串本身)
後綴:總是包含最後一個字符的子串(不包括父串本身)
————————————————————————————
舉個例子:
父串ABCDEAB
前綴:A、AB、ABC、ABCD、ABCDE、ABCDEA
後綴:B、AB、EAB、DEAB、CDEAB、BCDEAB
最長公共子綴就是指完全相同的最長前綴和後綴
在上述的例子中最長公共子綴是AB,長度爲2。
如下圖所示,最長公共子綴就是看前、後有多少完全相同的字符。
在這裏插入圖片描述

二、next數組

next數組的值其實就是【當前位置之前的字符串】的最長公共子綴長度
————————————————————————————
舉個例子:
父串ABAABACA
當我們在計算C字符對應的next數組值時,我們其實就是要計算ABAABA這個字符串的最長公共子綴。
ABAABA的前綴有A、AB、ABA、ABAA、ABAAB
                  後綴有A、BA、ABA、AABA、BAABA
最長的公共子綴是ABA,長度爲3,所以C這個位置的next數組值就是3。
由於第一個字符之前沒有任何字符,所以我們規定第一個字符處的next數組值爲-1;第二個字符之前只有1個字符,但是我們規定子綴是不能包含父串本身的,所以最長公共子綴爲0;其他位置的按照標準計算即可。
根據定義,父串ABAABACA的next數組值爲:

i 0 1 2 3 4 5 6 7
str[i] A B A A B A C A
next[i] -1 0 0 1 1 2 3 0

三、基於next數組的文本匹配

KMP算法解決的問題是在父串中查找子串是否存在。
————————————————————————————
我們舉例來說明KMP的工作過程
父串txt=BBCABCDABEABCDABCDABDE
子串str=ABCDABD
(1)求出子串str的next數組

i 0 1 2 3 4 5 6
str[i] A B C D A B D
next[i] -1 0 0 0 0 1 2

(2)指針定位到父串的開頭和子串的開頭,匹配第一個字符
在這裏插入圖片描述
(3)子串第一個字符就不匹配,子串不動,父串移動,繼續和子串第一個字符匹配
在這裏插入圖片描述
(4)子串第一個字符仍然不匹配,子串不動,父串移動,繼續和子串第一個字符匹配
在這裏插入圖片描述
(5)子串第一個字符仍然不匹配,子串不動,父串移動,繼續和子串第一個字符匹配
在這裏插入圖片描述
(6)子串第一個字符匹配了,則子串和父串同時向後移動,繼續匹配,直到匹配到子、父串不相等(或者子串匹配完成,則查找結束)
在這裏插入圖片描述
(7)匹配到不相等的位置,該位置在子串中的下標是i,則父串不動,子串向左移動i-next[i]個位置,比如圖中位置D在子串中下標是6,next[6]=2,則子串向左移動6-2=4個位置。
在這裏插入圖片描述
(8)繼續比較當前位置的子串是否和父串相等,如果等則子串、父串一起後移;如果不等則重複上面的過程,也就是左移i-next[i]個位置
在這裏插入圖片描述
(9)左移完之後繼續比較子串是否和父串相等,發現不等,這時發現子串已經回到了頭部,再左移沒有位置移動了,所以此時只移動父串,繼續和子串第一個位置比較
在這裏插入圖片描述
(10)發現父串和子串第一個位置相同,則父串、子串一起向後移動,直到匹配到不相同的字符(或者子串匹配完成,則查找結束)
在這裏插入圖片描述
(11)發現了不相等的字符,則子串向左移動i-next[i]個字符
在這裏插入圖片描述
(12)移動之後繼續匹配,如果相等就父串、子串一起往後動;如果不等就子串左移i-next[i]個字符;如果已經回退到子串頭部了,那就只移動父串就好
在這裏插入圖片描述
(13)子串已經匹配完了,查找結束。

所以KMP的基本工作原理就是:

1.如果子串在頭部,則:

  • 若子串、父串相等,則同時往後移動
  • 若子串、父串不等,則父串往後移動

2.如果子串不在頭部,則:

  • 若子串、父串相等,則同時往後移動
  • 若子串、父串不等,則子串往左移動i-next[i]個位置

四、KMP工作原理
你應該發現,KMP的核心要義其實就是左移i-next[i]這個部分了,那爲什麼這個部分可以幫助我們減少比較的工作量呢?
傳統的匹配算法如下圖:
在這裏插入圖片描述
在匹配到不相同的字符時,父串回退到剛纔開始匹配位置的下一個位置,子串回退到頭部,從頭開始匹配。
我們明顯覺得很麻煩,甚至可以喊一聲:你要退也退到一個跟子串開頭字符相等的位置吧,幹嘛非得一位一位動?

就比如下圖,我們在父串E位置匹配失敗了,如果要重新匹配,那肯定也是從剛纔匹配過的字符中找一個跟子串頭位置相等的位置,比如標紅的A處開始重新匹配吧,那爲什麼移動i-next[i]就正好能找到這個位置呢?
在這裏插入圖片描述
回顧一下next數組的含義:next[i]指的是【i之前的字符串】的最長公共子綴長度。

next數值告訴了我們兩個信息,一是子串從哪個位置開始和頭部相等,二是從那個位置開始,到當前位置結束,一共有多少個相同的字符和頭部是匹配的。
比如上圖位置D,next[6]=2,就代表str[6-2]=str[4]這個位置是跟頭部字符是相同的;而從str[4]這個位置開始,有2個字符和從頭部開始有2個字符時相同的。
從子串開頭開始有X0X1兩個字符和D前面兩個字符X4X5相等,既然X0X1=X4X5,現在X6和父串不相等,那我們下一步就應該看看X3和父串對應位置是否相等。

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