1.暴力匹配算法
假設現在我們面臨這樣一個問題:有一個文本串S,和一個模式串P,現在要查找P在S中的位置,怎麼查找呢?
如果用暴力匹配的思路,並假設現在文本串S匹配到 i 位置,模式串P匹配到 j 位置,則有:
- 如果當前字符匹配成功(即S[i] == P[j]),則i++,j++,繼續匹配下一個字符;
- 如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相當於每次匹配失敗時,i 回溯,j 被置爲0。
舉個例子,如果給定文本串S“BBC ABCDAB ABCDABCDABDE”,和模式串P“ABCDABD”,現在要拿模式串P去跟文本串S匹配,整個過程如下所示:
1. S[0]爲B,P[0]爲A,不匹配,執行第②條指令:“如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0”,S[1]跟P[0]匹配,相當於模式串要往右移動一位(i=1,j=0)
2. S[1]跟P[0]還是不匹配,繼續執行第②條指令:“如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0”,S[2]跟P[0]匹配(i=2,j=0),從而模式串不斷的向右移動一位(不斷的執行“令i = i - (j - 1),j = 0”,i從2變到4,j一直爲0)
3. 直到S[4]跟P[0]匹配成功(i=4,j=0),此時按照上面的暴力匹配算法的思路,轉而執行第①條指令:“如果當前字符匹配成功(即S[i] == P[j]),則i++,j++”,可得S[i]爲S[5],P[j]爲P[1],即接下來S[5]跟P[1]匹配(i=5,j=1)
4. S[5]跟P[1]匹配成功,繼續執行第①條指令:“如果當前字符匹配成功(即S[i] == P[j]),則i++,j++”,得到S[6]跟P[2]匹配(i=6,j=2),如此進行下去
5. 直到S[10]爲空格字符,P[6]爲字符D(i=10,j=6),因爲不匹配,重新執行第②條指令:“如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0”,相當於S[5]跟P[0]匹配(i=5,j=0)
6. 至此,我們可以看到,如果按照暴力匹配算法的思路,儘管之前文本串和模式串已經分別匹配到了S[9]、P[5],但因爲S[10]跟P[6]不匹配,所以文本串回溯到S[5],模式串回溯到P[0],從而讓S[5]跟P[0]匹配。
而S[5]肯定跟P[0]失配。爲什麼呢?因爲在之前第4步匹配中,我們已經得知S[5] = P[1] = B,而P[0] = A,即P[1] != P[0],故S[5]必定不等於P[0],所以回溯過去必然會導致失配。那有沒有一種算法,讓i 不往回退,只需要移動j 即可呢?
答案是肯定的。這種算法就是本文的主旨KMP算法,它利用之前已經部分匹配這個有效信息,保持i 不回溯,通過修改j 的位置,讓模式串儘量地移動到有效的位置。
3. KMP算法
3.1 定義
- 假設現在文本串S匹配到 i 位置,模式串P匹配到 j 位置
- 如果j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++,繼續匹配下一個字符;
- 如果j != -1,且當前字符匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]。此舉意味着失配時,模式串P相對於文本串S向右移動了j - next [j] 位。
- 換言之,當匹配失敗時,模式串向右移動的位數爲:失配字符所在位置 - 失配字符對應的next 值(next 數組的求解會在下文的3.3.3節中詳細闡述),即移動的實際位數爲:j - next[j],且此值大於等於1。
- ①尋找前綴後綴最長公共元素長度
- 對於P = p0 p1 ...pj-1 pj,尋找模式串P中長度最大且相等的前綴和後綴。如果存在p0 p1 ...pk-1 pk = pj- k pj-k+1...pj-1 pj,那麼在包含pj的模式串中有最大長度爲k+1的相同前綴後綴。舉個例子,如果給定的模式串爲“abab”,那麼它的各個子串的前綴後綴的公共元素的最大長度如下表格所示:
比如對於字符串aba來說,它有長度爲1的相同前綴後綴a;而對於字符串abab來說,它有長度爲2的相同前綴後綴ab(相同前綴後綴的長度爲k + 1,k + 1 = 2)。
- ②求next數組
- next 數組考慮的是除當前字符外的最長相同前綴後綴,所以通過第①步驟求得各個前綴後綴的公共元素的最大長度後,只要稍作變形即可:將第①步驟中求得的值整體右移一位,然後初值賦爲-1,如下表格所示:
比如對於aba來說,第3個字符a之前的字符串ab中有長度爲0的相同前綴後綴,所以第3個字符a對應的next值爲0;而對於abab來說,第4個字符b之前的字符串aba中有長度爲1的相同前綴後綴a,所以第4個字符b對應的next值爲1(相同前綴後綴的長度爲k,k = 1)。
- ③根據next數組進行匹配
- 匹配失配,j = next [j],模式串向右移動的位數爲:j - next[j]。換言之,當模式串的後綴pj-k pj-k+1, ..., pj-1 跟文本串si-k si-k+1, ..., si-1匹配成功,但pj 跟si匹配失敗時,因爲next[j] = k,相當於在不包含pj的模式串中有最大長度爲k 的相同前綴後綴,即p0 p1 ...pk-1 = pj-k pj-k+1...pj-1,故令j = next[j],從而讓模式串右移j - next[j] 位,使得模式串的前綴p0 p1, ..., pk-1對應着文本串 si-k si-k+1, ..., si-1,而後讓pk 跟si 繼續匹配。如下圖所示:
3.3 解釋
3.3.1 尋找最長前綴後綴
失配時,模式串向右移動的位數爲:已匹配字符數 - 失配字符的上一位字符所對應的最大長度值
下面,咱們就結合之前的《最大長度表》和上述結論,進行字符串的匹配。如果給定文本串“BBC ABCDAB ABCDABCDABDE”,和模式串“ABCDABD”,現在要拿模式串去跟文本串匹配,如下圖所示:
- 1. 因爲模式串中的字符A跟文本串中的字符B、B、C、空格一開始就不匹配,所以不必考慮結論,直接將模式串不斷的右移一位即可,直到模式串中的字符A跟文本串的第5個字符A匹配成功:
- 2. 繼續往後匹配,當模式串最後一個字符D跟文本串匹配時失配,顯而易見,模式串需要向右移動。但向右移動多少位呢?因爲此時已經匹配的字符數爲6個(ABCDAB),然後根據《最大長度表》可得失配字符D的上一位字符B對應的長度值爲2,所以根據之前的結論,可知需要向右移動6 - 2 = 4 位。
- 3. 模式串向右移動4位後,發現C處再度失配,因爲此時已經匹配了2個字符(AB),且上一位字符B對應的最大長度值爲0,所以向右移動:2 - 0 =2 位。
- 4. A與空格失配,向右移動1 位。
- 5. 繼續比較,發現D與C 失配,故向右移動的位數爲:已匹配的字符數6減去上一位字符B對應的最大長度2,即向右移動6 - 2 = 4 位。
- 6. 經歷第5步後,發現匹配成功,過程結束。
通過上述匹配過程可以看出,問題的關鍵就是尋找模式串中最大長度的相同前綴和後綴,找到了模式串中每個字符之前的前綴和後綴公共部分的最大長度後,便可基於此匹配。而這個最大長度便正是next 數組要表達的含義。
3.3.3 根據《最大長度表》求next 數組
由上文,我們已經知道,字符串“ABCDABD”各個前綴後綴的最大公共元素長度分別爲:
而且,根據這個表可以得出下述結論
- 失配時,模式串向右移動的位數爲:已匹配字符數 - 失配字符的上一位字符所對應的最大長度值
把next 數組跟之前求得的最大長度表對比後,不難發現,next 數組相當於“最大長度值” 整體向右移動一位,然後初始值賦爲-1。意識到了這一點,你會驚呼原來next 數組的求解竟然如此簡單:就是找最大對稱長度的前綴後綴,然後整體右移一位,初值賦爲-1(當然,你也可以直接計算某個字符對應的next值,就是看這個字符之前的字符串中有多大長度的相同前綴後綴)。
換言之,對於給定的模式串:ABCDABD,它的最大長度表及next 數組分別如下:
根據最大長度表求出了next 數組後,從而有
失配時,模式串向右移動的位數爲:失配字符所在位置 - 失配字符對應的next 值
而後,你會發現,無論是基於《最大長度表》的匹配,還是基於next 數組的匹配,兩者得出來的向右移動的位數是一樣的。爲什麼呢?因爲:
- 根據《最大長度表》,失配時,模式串向右移動的位數 = 已經匹配的字符數 - 失配字符的上一位字符的最大長度值
- 而根據《next 數組》,失配時,模式串向右移動的位數 = 失配字符的位置 - 失配字符對應的next 值
- 其中,從0開始計數時,失配字符的位置 = 已經匹配的字符數(失配字符不計數),而失配字符對應的next 值 = 失配字符的上一位字符的最大長度值,兩相比較,結果必然完全一致。
所以,你可以把《最大長度表》看做是next 數組的雛形,甚至就把它當做next 數組也是可以的,區別不過是怎麼用的問題。
3.3.4 通過代碼遞推計算next 數組
接下來,咱們來寫代碼求下next 數組。
基於之前的理解,可知計算next 數組的方法可以採用遞推:
- 1. 如果對於值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,相當於next[j] = k。
- 此意味着什麼呢?究其本質,next[j] = k 代表p[j] 之前的模式串子串中,有長度爲k 的相同前綴和後綴。有了這個next 數組,在KMP匹配中,當模式串中j 處的字符失配時,下一步用next[j]處的字符繼續跟文本串匹配,相當於模式串向右移動j - next[j] 位。
舉個例子,如下圖,根據模式串“ABCDABD”的next 數組可知失配位置的字符D對應的next 值爲2,代表字符D前有長度爲2的相同前綴和後綴(這個相同的前綴後綴即爲“AB”),失配後,模式串需要向右移動j - next [j] = 6 - 2 =4位。
向右移動4位後,模式串中的字符C繼續跟文本串匹配。
- 2. 下面的問題是:已知next [0, ..., j],如何求出next [j + 1]呢?
對於P的前j+1個序列字符:
- 若p[k] == p[j],則next[j + 1 ] = next [j] + 1 = k + 1;
- 若p[k ] ≠ p[j],如果此時p[ next[k] ] == p[j ],則next[ j + 1 ] = next[k] + 1,否則繼續遞歸前綴索引k = next[k],而後重複此過程。 相當於在字符p[j+1]之前不存在長度爲k+1的前綴"p0 p1, …, pk-1 pk"跟後綴“pj-k pj-k+1, …, pj-1 pj"相等,那麼是否可能存在另一個值t+1 < k+1,使得長度更小的前綴 “p0 p1, …, pt-1 pt” 等於長度更小的後綴 “pj-t pj-t+1, …, pj-1 pj” 呢?如果存在,那麼這個t+1 便是next[ j+1]的值,此相當於利用已經求得的next 數組(next [0, ..., k, ..., j])進行P串前綴跟P串後綴的匹配。
模式串的後綴:ABDE
模式串的前綴:ABC
前綴右移兩位: ABC
用代碼重新計算下“ABCDABD”的next 數組,以驗證之前通過“最長相同前綴後綴長度值右移一位,然後初值賦爲-1”得到的next 數組是否正確,計算結果如下表格所示:
從上述表格可以看出,無論是之前通過“最長相同前綴後綴長度值右移一位,然後初值賦爲-1”得到的next 數組,還是之後通過代碼遞推計算求得的next 數組,結果是完全一致的。
3.3.5 基於《next 數組》匹配
下面,我們來基於next 數組進行匹配。
還是給定文本串“BBC ABCDAB ABCDABCDABDE”,和模式串“ABCDABD”,現在要拿模式串去跟文本串匹配,如下圖所示:
在正式匹配之前,讓我們來再次回顧下上文2.1節所述的KMP算法的匹配流程:
- “假設現在文本串S匹配到 i 位置,模式串P匹配到 j 位置
- 如果j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++,繼續匹配下一個字符;
- 如果j != -1,且當前字符匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]。此舉意味着失配時,模式串P相對於文本串S向右移動了j - next [j] 位。
- 換言之,當匹配失敗時,模式串向右移動的位數爲:失配字符所在位置 - 失配字符對應的next 值,即移動的實際位數爲:j - next[j],且此值大於等於1。”
- 1. 最開始匹配時
- P[0]跟S[0]匹配失敗
- 所以執行“如果j != -1,且當前字符匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]”,所以j = -1,故轉而執行“如果j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++”,得到i = 1,j = 0,即P[0]繼續跟S[1]匹配。
- P[0]跟S[1]又失配,j再次等於-1,i、j繼續自增,從而P[0]跟S[2]匹配。
- P[0]跟S[2]失配後,P[0]又跟S[3]匹配。
- P[0]跟S[3]再失配,直到P[0]跟S[4]匹配成功,開始執行此條指令的後半段:“如果j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++”。
- P[0]跟S[0]匹配失敗
- 2. P[1]跟S[5]匹配成功,P[2]跟S[6]也匹配成功, ...,直到當匹配到P[6]處的字符D時失配(即S[10] != P[6]),由於P[6]處的D對應的next 值爲2,所以下一步用P[2]處的字符C繼續跟S[10]匹配,相當於向右移動:j - next[j] = 6 - 2 =4 位。
- 3. 向右移動4位後,P[2]處的C再次失配,由於C對應的next值爲0,所以下一步用P[0]處的字符繼續跟S[10]匹配,相當於向右移動:j - next[j] = 2 - 0 = 2 位。
- 4. 移動兩位之後,A 跟空格不匹配,模式串後移1 位。
- 5. P[6]處的D再次失配,因爲P[6]對應的next值爲2,故下一步用P[2]繼續跟文本串匹配,相當於模式串向右移動 j - next[j] = 6 - 2 = 4 位。
- 6. 匹配成功,過程結束。
匹配過程一模一樣。也從側面佐證了,next 數組確實是只要將各個最大前綴後綴的公共元素的長度值右移一位,且把初值賦爲-1 即可。
3.3.6 基於《最大長度表》與基於《next 數組》等價
我們已經知道,利用next 數組進行匹配失配時,模式串向右移動 j - next [ j ] 位,等價於已匹配字符數 - 失配字符的上一位字符所對應的最大長度值。原因是:
- j 從0開始計數,那麼當數到失配字符時,j 的數值就是已匹配的字符數;
- 由於next 數組是由最大長度值表整體向右移動一位(且初值賦爲-1)得到的,那麼失配字符的上一位字符所對應的最大長度值,即爲當前失配字符的next 值。
但爲何本文不直接利用next 數組進行匹配呢?因爲next 數組不好求,而一個字符串的前綴後綴的公共元素的最大長度值很容易求。例如若給定模式串“ababa”,要你快速口算出其next 數組,乍一看,每次求對應字符的next值時,還得把該字符排除之外,然後看該字符之前的字符串中有最大長度爲多大的相同前綴後綴,此過程不夠直接。而如果讓你求其前綴後綴公共元素的最大長度,則很容易直接得出結果:0 0 1 2 3,如下表格所示:
然後這5個數字 全部整體右移一位,且初值賦爲-1,即得到其next 數組:-1 0 0 1 2。
3.3.7 Next 數組與有限狀態自動機
next 負責把模式串向前移動,且當第j位不匹配的時候,用第next[j]位和主串匹配,就像打了張“表”。此外,next 也可以看作有限狀態自動機的狀態,在已經讀了多少字符的情況下,失配後,前面讀的若干個字符是有用的。
3.3.8 Next 數組的優化
行文至此,咱們全面瞭解了暴力匹配的思路、KMP算法的原理、流程、流程之間的內在邏輯聯繫,以及next 數組的簡單求解(《最大長度表》整體右移一位,然後初值賦爲-1)和代碼求解,最後基於《next 數組》的匹配,看似洋洋灑灑,清晰透徹,但以上忽略了一個小問題。
比如,如果用之前的next 數組方法求模式串“abab”的next 數組,可得其next 數組爲-1 0 0 1(0 0 1 2整體右移一位,初值賦爲-1),當它跟下圖中的文本串去匹配的時候,發現b跟c失配,於是模式串右移j - next[j] = 3 - 1 =2位。
右移2位後,b又跟c失配。事實上,因爲在上一步的匹配中,已經得知p[3] = b,與s[3] = c失配,而右移兩位之後,讓p[ next[3] ] = p[1] = b 再跟s[3]匹配時,必然失配。問題出在哪呢?
問題出在不該出現p[j] = p[ next[j] ]。爲什麼呢?理由是:當p[j] != s[i] 時,下次匹配必然是p[ next [j]] 跟s[i]匹配,如果p[j] = p[ next[j] ],必然導致後一步匹配失敗(因爲p[j]已經跟s[i]失配,然後你還用跟p[j]等同的值p[next[j]]去跟s[i]匹配,很顯然,必然失配),所以不能允許p[j] = p[ next[j ]]。如果出現了p[j] = p[ next[j] ]咋辦呢?如果出現了,則需要再次遞歸,即令next[j] = next[ next[j] ]。
所以,咱們得修改下求next 數組的代碼。
利用優化過後的next 數組求法,可知模式串“abab”的新next數組爲:-1 0 -1 0。可能有些讀者會問:原始next 數組是前綴後綴最長公共元素長度值右移一位, 然後初值賦爲-1而得,那麼優化後的next 數組如何快速心算出呢?實際上,只要求出了原始next 數組,便可以根據原始next 數組快速求出優化後的next 數組。還是以abab爲例,如下表格所示:
只要出現了p[next[j]] = p[j]的情況,則把next[j]的值再次遞歸。例如在求模式串“abab”的第2個a的next值時,如果是未優化的next值的話,第2個a對應的next值爲0,相當於第2個a失配時,下一步匹配模式串會用p[0]處的a再次跟文本串匹配,必然失配。所以求第2個a的next值時,需要再次遞歸:next[2] = next[ next[2] ] = next[0] = -1(此後,根據優化後的新next值可知,第2個a失配時,執行“如果j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++,繼續匹配下一個字符”),同理,第2個b對應的next值爲0。
對於優化後的next數組可以發現一點:如果模式串的後綴跟前綴相同,那麼它們的next值也是相同的,例如模式串abcabc,它的前綴後綴都是abc,其優化後的next數組爲:-1 0 0 -1 0 0,前綴後綴abc的next值都爲-1 0 0。
然後引用下之前3.1節的KMP代碼:
接下來,咱們繼續拿之前的例子說明,整個匹配過程如下:
1. S[3]與P[3]匹配失敗。
2. S[3]保持不變,P的下一個匹配位置是P[next[3]],而next[3]=0,所以P[next[3]]=P[0]與S[3]匹配。
3. 由於上一步驟中P[0]與S[3]還是不匹配。此時i=3,j=next [0]=-1,由於滿足條件j==-1,所以執行“++i, ++j”,即主串指針下移一個位置,P[0]與S[4]開始匹配。最後j==pLen,跳出循環,輸出結果i - j = 4(即模式串第一次在文本串中出現的位置),匹配成功,算法結束。
3.4 KMP的時間複雜度分析
- 如果模式串中存在相同前綴和後綴,即pj-k pj-k+1, ..., pj-1 = p0 p1, ..., pk-1,那麼在pj跟si失配後,讓模式串的前綴p0 p1...pk-1對應着文本串si-k si-k+1...si-1,而後讓pk跟si繼續匹配。
- 之前本應是pj跟si匹配,結果失配了,失配後,令pk跟si匹配,相當於j 變成了k,模式串向右移動j - k位。
- 因爲k 的值是可變的,所以我們用next[j]表示j處字符失配後,下一次匹配模式串應該跳到的位置。換言之,失配前是j,pj跟si失配時,用p[ next[j] ]繼續跟si匹配,相當於j變成了next[j],所以,j = next[j],等價於把模式串向右移動j - next [j] 位。
- 而next[j]應該等於多少呢?next[j]的值由j 之前的模式串子串中有多大長度的相同前綴後綴所決定,如果j 之前的模式串子串中(不含j)有最大長度爲k的相同前綴後綴,那麼next [j] = k。
“KMP的算法流程:
- 假設現在文本串S匹配到 i 位置,模式串P匹配到 j 位置
- 如果j = -1,或者當前字符匹配成功(即S[i] == P[j]),都令i++,j++,繼續匹配下一個字符;
- 如果j != -1,且當前字符匹配失敗(即S[i] != P[j]),則令 i 不變,j = next[j]。此舉意味着失配時,模式串P相對於文本串S向右移動了j - next [j] 位。”
我們發現如果某個字符匹配成功,模式串首字符的位置保持不動,僅僅是i++、j++;如果匹配失配,i 不變(即 i 不回溯),模式串會跳過匹配過的next [j]個字符。整個算法最壞的情況是,當模式串首字符位於i - j的位置時才匹配成功,算法結束。
所以,如果文本串的長度爲n,模式串的長度爲m,那麼匹配過程的時間複雜度爲O(n),算上計算next的O(m)時間,KMP的整體時間複雜度爲O(m + n)。
4. 擴展1:BM算法
KMP的匹配是從模式串的開頭開始匹配的,而1977年,德克薩斯大學的Robert S. Boyer教授和J Strother Moore教授發明了一種新的字符串匹配算法:Boyer-Moore算法,簡稱BM算法。該算法從模式串的尾部開始匹配,且擁有在最壞情況下O(N)的時間複雜度。在實踐中,比KMP算法的實際效能高。
BM算法定義了兩個規則:
- 壞字符規則:當文本串中的某個字符跟模式串的某個字符不匹配時,我們稱文本串中的這個失配字符爲壞字符,此時模式串需要向右移動,移動的位數 = 壞字符在模式串中的位置 - 壞字符在模式串中最右出現的位置。此外,如果"壞字符"不包含在模式串之中,則最右出現位置爲-1。
- 好後綴規則:當字符失配時,後移位數 = 好後綴在模式串中的位置 - 好後綴在模式串上一次出現的位置,且如果好後綴在模式串中沒有再次出現,則爲-1。
下面舉例說明BM算法。例如,給定文本串“HERE IS A SIMPLE EXAMPLE”,和模式串“EXAMPLE”,現要查找模式串是否在文本串中,如果存在,返回模式串在文本串中的位置。
1. 首先,"文本串"與"模式串"頭部對齊,從尾部開始比較。"S"與"E"不匹配。這時,"S"就被稱爲"壞字符"(bad character),即不匹配的字符,它對應着模式串的第6位。且"S"不包含在模式串"EXAMPLE"之中(相當於最右出現位置是-1),這意味着可以把模式串後移6-(-1)=7位,從而直接移到"S"的後一位。
2. 依然從尾部開始比較,發現"P"與"E"不匹配,所以"P"是"壞字符"。但是,"P"包含在模式串"EXAMPLE"之中。因爲“P”這個“壞字符”對應着模式串的第6位(從0開始編號),且在模式串中的最右出現位置爲4,所以,將模式串後移6-4=2位,兩個"P"對齊。
3. 依次比較,得到 “MPLE”匹配,稱爲"好後綴"(good suffix),即所有尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好後綴。
4. 發現“I”與“A”不匹配:“I”是壞字符。如果是根據壞字符規則,此時模式串應該後移2-(-1)=3位。問題是,有沒有更優的移法?
5. 更優的移法是利用好後綴規則:當字符失配時,後移位數 = 好後綴在模式串中的位置 - 好後綴在模式串中上一次出現的位置,且如果好後綴在模式串中沒有再次出現,則爲-1。
所有的“好後綴”(MPLE、PLE、LE、E)之中,只有“E”在“EXAMPLE”的頭部出現,所以後移6-0=6位。
可以看出,“壞字符規則”只能移3位,“好後綴規則”可以移6位。每次後移這兩個規則之中的較大值。這兩個規則的移動位數,只與模式串有關,與原文本串無關。
6. 繼續從尾部開始比較,“P”與“E”不匹配,因此“P”是“壞字符”,根據“壞字符規則”,後移 6 - 4 = 2位。因爲是最後一位就失配,尚未獲得好後綴。
由上可知,BM算法不僅效率高,而且構思巧妙,容易理解。