字符串匹配算法


    KMP算法,以為一個簡簡單單的算法,看了我一天時間竟然沒有看懂...果然圖樣圖撕破了,三位大師提出的算法豈是我等屌絲能夠迅速的理解的?不過話說看了這次算法才知自己的智力有多么的吃緊,還是要努力學習呀~智力不行就要加把勁了。(接下來字符串匹配算法均參考算法導論)

    字符串匹配,算法的模型不用提出大家都知道,僅僅是在文本T字符串中精確匹配模式串P,簡簡單單,輕輕松松的就知道這么一個模型,自然而然的能夠想到一個最笨最實用的算法,朴素算法,朴素算法就是逐個比對,然后在文本串中下移一位在進行逐個比對,算法復雜度O((n-m+1)m),

    不過這種簡單的方法的時間復雜度不是我們能夠容忍的,提高一點有一個Rabin-Karp的算法,此算法的最壞時間復雜度沒有提高,但是平均情況比較好,而且它這個思想可以借鑒,就是把匹配當做算術運算,后來取模比對,針對剩余的少部分精確比對~不過這不是重點,

    接下來的就是重量級的算法,KMP算法,它在預處理時間和匹配時間均達到了線性時間~O(m)的預處理時間,O(n)的匹配時間,非常完美的理論,不是么?網上很多資料都是關於next函數的,這里我參考算法導論里面的KMP算法,其思想雖然一致,但是不同於next函數的原理,其原理如下:(匹配過程)

 

 i  1 2 3 4 5 6

    a b a b a b a a b a b 

    a b a b a c b

          a b a b a c b  

        j 1 2 3 4 5 6 7  

  j 1 2 3 4 5 6

 

    這里比對過程是i和j的變化,如果i和j對應的地方相同,則同時+1,繼續走下去,當出現T[i]和P[j]出現不同時,如上圖中的P[6]!=T[6],此時KMP考慮的是i不進行回溯,而是考慮挪動j,使得j變化為一個更小的值,小於j,這樣的變化效果是P整體向后移,但是此時不能夠導致T[6]前面的i的匹配性質,及挪動后T[6]前面的元素和P對齊之后還是要相同的,所以這里j=5改為j=3,挪動后效果如紅色標注的P所示,這是T[6]=P[4],兩個串的表示i和j可以挪動下去;

 i  1 2 3 4 5 6 7 8 9 10 11

    a b a b a b a a b a b 

     

          a b a b a c b  

                a b a b a c b

                      3

                      a b a b a c b

                      1

                         a b a b a c b

                      0

        j 1 2 3 4 5 6 7  

    但是接下來挪動的時候又產生了問題,T[8]!=P[6],這時候如同上面的方式,挪動j=5的位置,首先挪動至最近的保持T[8]前保持性質的部分,其實這里保持性質及P[5]的后綴的最長前綴...說到概念可能就不知道了,還是理解保持性質吧,挪動至紅色后發現T[8]!=P[4],所以這里還要挪動j=3的位置,挪動成j=1,還是不能滿足,所以還需要挪動,這時j=0了,及這時候T[8]和P[1]比較了,這里剛好一樣,所以i可以繼續向后挪動了;

    以上的移動均是參考了一個數字,該數字是保證某個位置的時候P和T能夠保持性質,但是這個性質僅僅和P有關,及上面說的P的后綴的最長前綴,因為以上匹配過程用到了這個數據,所以接下來就是這個數據的計算的算法了,其實這個算法才是讓我糾結的地方,糾結了好久,這里發現一個可以很容易理解的地方,分享給大家;

    上面算法的線性時間需要用到均攤分析的知識~可以仔細思考一下,線性的時間,

    這里圖解一下計算后綴的最長前綴吧,這里這個記為PI[j],這里PI[j]可以根據PI[1],PI[2]...PI[j-1]計算得出,

    

    如圖所示,PI[j-1]為標記的凸包的長度,這里如果P[lengh[PI[j-1]]+1]=P[j],這里PI[j]及為PI[j-1]長度加1,如果P[lengh[PI[j-1]]+1]!=P[j],則這個需要在更加前面找,這里及遞歸的找,畫圖如下,

    

    及黑色線標注的位置是否與P[j]相同選擇是否繼續遞歸,黑色標注的位置的選擇為PI[ PI[j-1] ] + 1,PI[j-1]的位置為紅色的那段,因為不可能為紅色,所以要縮小一下,這里示意為綠色的圈,綠色的圈挪動到前邊的紅色部位,所以綠色的圈在紅色的部位找到前綴為黑色的部分,所以PI[ PI[j-1] ]是這樣得出的,這樣遞歸下去就能夠得出PI[j]的值了,這樣就可以根據計算得出的PI[j]值運用匹配算法了,

    不知道上面的解釋是否能夠解釋的清楚,一個遞歸的過程,分析算法復雜度也是一個均攤分析的方法,同樣是線性的,

 

    模式串的預處理的偽代碼如下:

COMPUTE-PREFIX-FUNCTION(P)
    m=length(P) 
    PI[1]=0               //初始化
    k=0                    //初始化
    for q=2 to m
        while k>0 and P[k+1]!=P[q] //結束條件至0或者相等
            k=PI[k]                             //遞歸的過程
        if P[k+1]=P[q]      //               增加的過程
            k=k+1
        PI[q]=k                         //賦值為k,下一個計算已k為起點向下遞歸
    return PI

  KMP匹配過程執行的偽代碼如下:

KMP-MATCH(T,P)
    n = length[T]
    m = length[P]
    PI=COMPUTE-PREFIX-FUNCTION(P)
    j=0
    for i=1 to n
        while j>0 and P[j+1]!=T[i]  //如果不等,挪動P,根據計算好的PI
            j=PI[j]
        if P[j+1]=T[i] //如果相等,則向后繼續匹配
            j=j+1
        if j=m            //匹配m個后則成功
            printf(匹配成功)
            j = PI[j]
       

  以上是偽代碼描述,上面說描述的參考算法導論的KMP思想,網上還有一種next方式的,有空研讀一下比較差別;

     除了上述算法還有一些優秀的字符串匹配算法,基於自動機的,BM算法等等,

 如果想要了解一下有限自動機方法可以繼續看下去,這種方法可KMP算法可以看做等價,

 有限自動機字符串匹配算法簡單介紹如下:

給定模式P[1...m],其對應的字符串匹配自動機定義如下:

1、狀態集Q為{0,1,...,m},初始狀態q0為0狀態,狀態m是唯一的接受狀態。

2、對於任意的狀態q和字符a,變遷函數δ由如下等式定義:δ(q,a)=σ(Pq a)

這里的解釋一下σ(x)的定義,其為相應P的后綴函數。σ(x)為x的后綴 P的最長前綴的長度:σ(x)=max{k:Pk是x的后綴}

簡單的舉例如下:

i         1 2 3 4 5 6 7 8 9 10 11 

T[i]    a  b a b a b a c  a  b   a 

狀態   1 2  3 4 5  4 5 6  7 2  3  

上述是匹配的過程,下面是轉移表:

      a    b     c              P

0    1     0     0            a    

1    1     2     0            b

2    3     0     0            a

3    1     4     0            b

4    5     0     0            a

5    1     4     6            c

6    7     0     0            a

7    1     2     0 

自動機的過程與KMP可以轉換,只不過自動機考慮了T[i]不匹配過程中的具體字母表的轉移,然后KMP只是不匹配的情況下先進行挪動,挪動后發現不匹配之后再進行挪動?直至挪動為0的時候,抑或是一直不匹配i++,此時忽略j;自動機只是具體考慮了此時如果T[i]如果不等於P[j],並且此時T[i]的具體取值已經指定,在此值指定的情況下應該怎樣的轉移~

a  b  a  b  a  c  a

0  0  1  2  3  0  1

上面是KMP計算出的模式函數,首先a值是T[i]和P[1]如果相等,則j++,向后移動,但是如果不匹配,此時如同自動機轉移表第一行,為b or c,此時就應該i,j同時++了,此時KMP如果發現T[i]和P[1]不相等,則上面算法i++,j取值仍為0,等價於i和j同時++了;

這時考慮b,如果a匹配了,P[2]和T[i]匹配,此時j++=2了,這時候移動兩位了,自動機表現匹配兩個字母,所以為狀態2,但是如果不相等呢?此時考慮了b,必定T[i]和P[1]已經匹配了,KMP中不匹配則考慮移動P,根據值j重新改為0,及向右移動兩位,然后自動機此時讀入不等的可能為a或者c,如果為a,則KMP移動兩位之后匹配上了一位,j++=1,如果為c,移動之后仍然不能匹配,所以i和j同時仍然移動,KMP表現i++,j重新賦值為0;

...

這次考慮一個中間的值,比如c的位置,如果知道T[i]值為多少,則可以知道具體的移動多少,然而KMP中是根據P計算的模式值,所以這里不知道T[i]的值,它有可能是最壞的值,所以這里取得的是最壞的值;所以這里計算自動機轉移函數可以考慮KMP中已經計算好的結果,這樣提升時間復雜度,

...

所以自動機匹配和KMP算法有一定的等價性,上面的描述比較凌亂,希望能夠對你有所啟發,


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM