1. 程式人生 > >BF演算法和KMP演算法對比

BF演算法和KMP演算法對比

BF演算法和KMP演算法

    子串的定位操作通常被稱作串的模式匹配,所謂的模式匹配即從較長的字串S(主串)的pos位置處開始尋找字串P(模式串)首次出現的位置,模式匹配演算法經典的有BF演算法和KMP演算法。

1、BF演算法

    BF演算法的思想是從主串的第pos個字元起和模式的第一字元比較,若相等,則繼續比較後續字元;若主串與模式串不是完全匹配,則回溯至主串的第pos+1個字元起和模式的第一個字元比較,若相等,則繼續比較後續字元,否則繼續重複上述回溯機制,直至找到主串中第一次包含子串的的位置,其圖例如下:

    因此,BF演算法如下所示:



2、KMP演算法引文——BF演算法分析

    由上面的分析可知,BF演算法是基於主串指標回溯,重新與子串進行逐字元進行比較,主串為S什麼要進行回溯呢,原因在於模式P中存在相同的字元或者說由字元(串)存在重複(模式的部分匹配性質),設想如果模式P中字元各不相同,主串就S的指標就根本不需要回溯;然而,我們可以發現在主串S與模式發生失配時,主串指標進行回溯會影響效率,因為由於模式S本身字元的部分部分匹配性質,回溯之後,主串S與模式P有些部分比較是沒有必要的,這就是對BF演算法所要改進的地方。

3、KMP演算法原理

    KMP演算法就是針對BF演算法的缺點所提出的,其關鍵就是,主串S與模式P發生失配時,如果主串S指標不進行回溯,那麼下面應該與模式中那個位置的字元進行比較呢?一旦在模式中找到這樣的位置,說明我們不需要每次與模式P重新開始匹配,這就意味著KMP演算法會直接跨過較多的位數,起到加速的作用。

    從上面的分析我們可以隱約感覺到,當S[i]與P[j]發生失配時,與S[i]進行下一次比較的P[next(j)](next(j)表示發生失配時,與S[i]進行的比較的模式P中的字元位置),是由模式P本身固有的性質決定的,即上文2所提的BF回溯的原因,模式P本身的部分匹配性質。這就意外著,只要模式P給定,模式P與任意給定的主串發生失配時,模式P每一位對應的跳轉位置是固定的,即可以事先確定。用數學語言來說就是,模式P通過自身的部分匹配性質映射了一個相同長度的跳轉位置陣列(專業叫字尾陣列,文中用next陣列表示),下面我們首先分析確定next陣列的數學原理。

S:S0, S1, S2,…, Si-j

, Si-j+1, …………….., Si-1, Si, Si+1,…….., Sn

P:           P0, P1,P2, …………......, Pj-1, Pj,………,Pm

P:                   P0, P1, P2, ......, Pk-1,Pk,……,Pj-k, Pj-k+1…, Pj-1,Pj, ………Pm

    假設S[i]與P[j]發生失配(以紅色表示),我們找到了跳轉的位置k,即next[j]=k(藍色表示),則可以得到下面幾個數學關係:

    失配時:P[0,1,2……,j-2,j-1] = S[i-j,i-j+1,…….,i-2,i-1];(1)

    跳轉後:P[0,1,2……,k-2,k-1] = S[i-k,i-k+1,…….,i-2,i-1];(2)

    結合(1)、(2)得:

S[i-k,i-k+1,…….,i-2,i-1]= P[j-k,j-k+1,……,j-2,j-1] = P[0,1,2……,k-2,k-1];(3)

    由(3)式可知,P[j-k,j-k+1,……,j-2,j-1] = P[0,1,2……,k-2,k-1],等號左右兩邊恰好是P[0,1,2……,j-2,j-1]的字首和字尾的相等部分(好好體會字首和字尾概念),且這個相等部分的長度等於k即next[j]。

    由上數學分析可知,KMP演算法每一次匹配都是基於前一次匹配的結果(前一次的結果豐富記錄了模式P的性質),那麼我們就要在前一次基礎上,分析模式P的重複特性,找出字首和字尾相同部分的長度求出next陣列,下面給出next陣列的遞推演算法。

4、next陣列與KMP演算法

    我們看到,求取next陣列是KMP演算法的核心,我們假設已知k=next[j],即有:

P[j-k,j-k+1,……,j-2,j-1]= P[0,1,2……,k-2,k-1]

則S[i+1]與P[j+1]發生失配時:

    (1)P[j]=P[k],則表明模式P中:

P[j-k,j-k+1,……,j-2,j-1,j]= P[0,1,2……,k-2,k-1,k]

顯然,我們容易得到:next[j+1] = k + 1=next[j]+ 1;

    (2)P[j]!=P[k],則表明模式串P中:

P[j-k,j-k+1,……,j-2,j-1,j]!= P[0,1,2……,k-2,k-1,k]

此時將求next陣列的問題轉化一個模式匹配問題,由上式的形式可以看出,將模式串P既看做主串有看做模式串,則在當前的模式匹配中,已經有:

P[j-k,j-k+1,……,j-2,j-1]= P[0,1,2……,k-2,k-1]

則由(1)的思想,若next[k]=k′且P[j]=P[k′],則:

P[j- k′,j- k′+1,……,j-2,j-1,j]= P[0,1,2……, k′-2, k′-1, k′]

    這就是說,next[j+1]= k′ + 1 = next[k] + 1。同理,若P[j]!=P[k′],繼續按執行(2)的思想執行。

    按照上述遞推思想得到獲取next陣列的演算法如下:

    上面的演算法還任然有缺陷,若S[i]與P[j]發生失配時,若出現,P[j]=P[next(j)]時,還按照上述演算法進行,仍然不是最有效率的,此時可以跳過更多的步數,具體修改如下:

    則完整的KMP演算法如下:


5、時間和空間複雜度分析

演算法

時間複雜度

空間複雜度

BF

slen*plen

1

KMP

slen+plen

plen