字符串匹配算法系列一:KMP算法原理

本文主要參考了https://mp.weixin.qq.com/s/rbaPmBejID8-rYui35Snrg的表述,加上部分本身的理解算法

學習任何算法都要了解該算法解決什麼問題?咱們看看KMP算法主要解決什麼問題。咱們舉一個例子,已知字符串1(ABCABBABAABBA)中查找字符串2(ABCAAB)是否存在,若是存在,字符串2在字符串1中的起始位置是多少?學習

對於上面這個列子,咱們很快就能夠回答出來字符串2(ABCAAB)在字符串1中存在,起始位置爲5(假設從0開始)字體

暴力匹配:ui

不考慮任何效率的問題,固然就是從頭至尾比較過去了,好比:spa

ABCABBABAABBA遊戲

ABCAAB→字符串

比較下來,發現從第4個字符開始不一樣了,那沒辦法,說明前5個字符不是咱們想要的字串,接下來咱們就讓字符串往前拱一步,繼續比較:效率

ABCABBABAABBA循環

  ABCAAB→方法

這一輪更慘,第一個就栽了,不要緊,繼續死磕往前拱:

ABABCBABAABBA

     ABCAAB→

這一輪也不咋滴,比較到第3個就歇菜了。只要咱們有毅力,這個過程堅持不懈,總能找到(或者找不到)答案。

暴力求解算法的複雜度爲:假設長字符串爲n,短字符串爲m,在最極端的狀況下(只有最後m個字符才互相匹配)咱們須要走n-m輪,每一輪須要匹配m次,時間複雜度爲O(nm)

 

如今的問題是:有沒有更快的方法來找到匹配的字串呢?

KMP算法

 KMP算法的核心意思就是:當咱們發現一次比較下來字串沒有徹底匹配的狀況下,下一次的比較也許能夠不止往前拱一步,也許能夠拱N步,關鍵是,究竟能夠拱幾步呢?

 

搞清楚這個問題前,先來搞清楚一個關鍵信息:「部分匹配值」,官方的解釋太囉嗦了,我掰開了揉碎了講給你聽,就是這樣:將咱們要匹配的字串寫出來,寫兩遍:

ABCAAB→

                 ←ABCAAB

看到了吧,咱們讓他們一個向左一個向右,相向而行。算一下上一行的右邊跟下一行的左邊,最多能有幾個字符是重合的(注意上一行的首字符不參與這個遊戲,不然每一個字串都是從頭配到尾,就沒意思了),這個數值N就是字串ABAAB的所謂部分匹配值,固然,你會發現此時N等於2,由於:

 

        後綴

ABCAAB

      ←ABCAAB

          前綴

下邊的字串再往前走,再也找不到更多的重合的字符了,所以字串"ABAAB"的部分匹配值爲2,記爲:

ABCAAB

          2

 

來,繼續算部分匹配值,接下來縮短一點,將最右邊的B去掉,來看ABAA的部分匹配值:

ABCAA

       ←ABCAA

顯而易見,字串"ABAA"的部分匹配值是1,記爲:

ABCAAB

          1 2

不斷重複上述過程,將每個字串的「部分匹配值」都算出來,就是所謂的部分匹配表,以下所示。

ABCAAB

00 01 12

 

好了,花開兩朵各表一枝,說回剛剛提到的問題:發現不匹配以後,究竟要向右拱幾步呢?答案是:能夠往前拱 (N-x) 步。N指的是匹配的字符數,x是部分匹配值,如今咱們再把剛開始的步驟再作一遍:

ABCABBABAABBA

ABCAAB→

比較下來發現有3個字符匹配,此時N=4,查表得知ABCA的部分匹配值x=1,所以咱們此時能夠往前拱 (4-1)步,接下來比較:

ABCABBABAABBA

       ABCAAB→

       

 

  我想你們心中必定有這樣一個疑問,爲何能夠這樣子移動,這樣子移動不會漏掉匹配數據嗎?

 

上面一行的紅色字體ABCA的最後一個A其實就是ABCA的最長後綴

ABCABBABAABBA

        ABCAAB→

下面一行的紅色字體的ABCA的第一個字符A其實就是最長前綴

咱們移動的目的就是要讓最長後綴(A)與最長的前綴(A)重合,也就是讓兩頭的部分匹配的區段重合在一塊兒

爲何能夠這樣子移動?

由於咱們知道ABCA先後綴匹配部分只有A,也就是說ABCABBABAABBA的前綴AB與ABCA的後綴CA是不匹配的。

若是咱們把小字符串ABCAAB中的A移動到與ABCABBABAABBA中A前面任何一個位置,好比C位置獲得的上下字符必定不匹配,由於它們公共部分只有A

ABCABBABAABBA

     ABCAAB

         

你發現,此時咱們就跳過了一些比較循環,讓咱們整個算法的效率獲得極大的提高。其實KMP算法的核心,就是充分利用比較過的未能匹配的字串的信息,而不是一股腦將他們丟棄。

相關文章
相關標籤/搜索