KMP算法理解

生氣生氣這次真的理解了。。。

看下面。。。。。。

1.

  首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一個字符與搜索詞"ABCDABD"的第一個字符,進行比較。因爲B與A不匹配,所以搜索詞後移一位。

  2.

  因爲B與A不匹配,搜索詞再往後移。

  3.

  就這樣,直到字符串有一個字符,與搜索詞的第一個字符相同爲止。

  4.

  接着比較字符串和搜索詞的下一個字符,還是相同。

  5.

  直到字符串有一個字符,與搜索詞對應的字符不相同爲止。

  6.

  這時,最自然的反應是,將搜索詞整個後移一位,再從頭逐個比較。這樣做雖然可行,但是效率很差,因爲你要把"搜索位置"移到已經比較過的位置,重比一遍。

  7.

  一個基本事實是,當空格與D不匹配時,你其實知道前面六個字符是"ABCDAB"。KMP算法的想法是,設法利用這個已知信息,不要把"搜索位置"移回已經比較過的位置,繼續把它向後移,這樣就提高了效率。

  8.

  怎麼做到這一點呢?可以針對搜索詞,算出一張《部分匹配表》(Partial Match Table)。這張表是如何產生的,後面再介紹,這裏只要會用就可以了。

  9.

  已知空格與D不匹配時,前面六個字符"ABCDAB"是匹配的。查表可知,最後一個匹配字符B對應的"部分匹配值"爲2,因此按照下面的公式算出向後移動的位數:

  移動位數 = 已匹配的字符數 - 對應的部分匹配值

  因爲 6 - 2 等於4,所以將搜索詞向後移動4位。

  10.

  因爲空格與C不匹配,搜索詞還要繼續往後移。這時,已匹配的字符數爲2("AB"),對應的"部分匹配值"爲0。所以,移動位數 = 2 - 0,結果爲 2,於是將搜索詞向後移2位。

  11.

  因爲空格與A不匹配,繼續後移一位。

  12.

  逐位比較,直到發現C與D不匹配。於是,移動位數 = 6 - 2,繼續將搜索詞向後移動4位。

  13.

  逐位比較,直到搜索詞的最後一位,發現完全匹配,於是搜索完成。如果還要繼續搜索(即找出全部匹配),移動位數 = 7 - 0,再將搜索詞向後移動7位,這裏就不再重複了。

  14.

  下面介紹《部分匹配表》是如何產生的。

  首先,要了解兩個概念:"前綴"和"後綴"。 "前綴"指除了最後一個字符以外,一個字符串的全部頭部組合;"後綴"指除了第一個字符以外,一個字符串的全部尾部組合。

  15.

                      next數組的含義

                  重點來了。下面解釋一下next數組的含義,這個也是KMP算法中比較不好理解的一點。

                   令原始串爲: S[i],其中0<=i<=n;模式串爲: T[j],其中0<=j<=m

                 假設目前匹配到如下位置

               S0,S1,S2,...,Si-j,Si-j+1...............,Si-1Si, Si+1,....,Sn

                                   T0,T1,.....................,Tj-1Tj, ..........

                             ST的綠色部分匹配成功,恰好到SiTj的時候失配,如果要保持i不變,同時達到讓模式串T相對於原始串S右移的話,可以更新j的值,讓Si和新的Tj進行匹配,假設新的jnext[j]表示,即讓Si和                    next[j]匹配,顯然新的j值要小於之前的j值,模式串纔會是右移的效果,也就是說應該有next[j] <= j -1。那新的j值也就是next[j]應該是多少呢?我們觀察如下的匹配:

      1)如果模式串右移1位(從簡單的思考起,移動一位會怎麼樣),即next[j] = j - 1, 即讓藍色的SiTj-1匹配(注:省略號爲未匹配部分)

               S0,S1,S2,...,Si-j,Si-j+1...............,Si-1Si, Si+1,....,Sn

                                   T0,T1,.....................,Tj-1Tj, .......... (T的劃線部分和S劃線部分相等【1】)

                                        T0,T1,.................Tj-2,Tj-1, ....... (移動後的T的劃線部分和S的劃線部分相等【2】)

        根據【1】【2】可以知道當next[j] =j -1,即模式串右移一位的時候,有T[0 ~ j-2] == T[1 ~ j-1],而這兩部分恰好是字符串T[0 ~j-1]的前綴和後綴,也就是說next[j]的值取決於模式串Tj前面部分的前綴和後綴相等部    分的長度(好好揣摩這兩個關鍵字概念:前綴、後綴,或者再想想,我的上一篇文章,從Trie樹談到後綴樹中,後綴樹的概念)。

      2)如果模式串右移2位,即next[j] = j - 2, 即讓藍色的SiTj-2匹配    

               S0,S1,...,Si-j,Si-j+1,Si-j+2...............,Si-1Si, Si+1,....,Sn

                                   T0,T1,T2,.....................,Tj-1Tj, ..........(T的劃線部分和S劃線部分相等【3】)

                                              T0,T1,...............,Tj-3,Tj-2,.........(移動後的T的劃線部分和S的劃線部分相等【4】)

        同樣根據【3】【4】可以知道當next[j] =j -2,即模式串右移兩位的時候,有T[0 ~ j-3] == T[2 ~ j-1]。而這兩部分也恰好是字符串T[0 ~j-1]的前綴和後綴,也就是說next[j]的值取決於模式串Tj前面部分的前綴和後綴相等部分的長度

     3)依次類推,可以得到如下結論:當發生失配的情況下,j的新值next[j]取決於模式串中T[0 ~ j-1]中前綴和後綴相等部分的長度, 並且next[j]恰好等於這個最大長度

   4)按照遞推的思想:

     根據定義next[0]=-1,假設next[j]=k, 即P[0...k-1]==P[j-k,j-1]

     1)若P[j]==P[k],則有P[0..k]==P[j-k,j],很顯然,next[j+1]=next[j]+1=k+1;

     2)若P[j]!=P[k],則可以把其看做模式匹配的問題,即匹配失敗的時候,k值如何移動,顯然k=next[k]。



  16.

  "部分匹配"的實質是,有時候,字符串頭部和尾部會有重複。比如,"ABCDAB"之中有兩個"AB",那麼它的"部分匹配值"就是2("AB"的長度)。搜索詞移動的時候,第一個"AB"向後移動4位(字符串長度-部分匹配值),就可以來到第二個"AB"的位置。

   17.模式串右移1,即next[j] = j - 1(記住這一點)吐舌頭吐舌頭吐舌頭吐舌頭


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