再談KMP BM演算法(II)

2021-07-10 10:59:41 字數 2338 閱讀 7408

我在《kmp演算法詳解》一文中已經介紹了next[i]的含義,對於s[i],next[i]的意義是,如果存在k使得s[1...i-k]=s[k...i-1]且s[i-k+1]!=s[i],那麼next[i]=i-k+1。實際上對於滿足條件的k,其z值zk(s)就滿足k+zk(s)=i,next[i]=zk(s)+1,所以我們可以用如下方法根據模式串s的zi(s)表填寫對應的next[i]表。

規則一,從頭到尾遍歷zi(s),當遍歷到元素k時,如果zk(s)!=0,那麼next[k+zk(s)]=zk(s)+1,如果還存在k'使得k+zk(s)=k'+zk'(s)那麼next[k+zk(s)]等於zk(s)+1與zk'(s)+1的較大者。

規則二,對於遍歷zi(s)列表之後,尚未填寫的元素的next值,我們按照如下原則填充,對於元素s[i],如果s[i]=s[1],則其next值next[i]=0,否則next[i]=1。

根據上面的原則,我們對於《kmp演算法詳解》中的老例子通過zi(s)構建next[i]的**如下。這裡對於s[8],由於4+z4(s)=8,7+z7(s)=8,所以我們選擇其中的較大者z4(s)=4,令next[8]=z4(s)+1=5。對於s[9],由於9+z9(s)超出了pattern陣列的範圍,所以我們不使用該z值計算next跳轉表。實際對於下表,除了next[8]之外,其餘均是由規則二填寫。相較於kmp三人給出的next表填寫演算法,利用z值表填寫next表固然增加了乙個轉換層,降低了演算法效率,但是從易理解的角度講,由z值到next值的轉換是十分有意義的。12

3456

78910

patternab

cabc

acab

zi(s)00

0400

1020

next01

1011

0501

用z值表填寫goodsuffix表的過程,要比填寫next表複雜得多。首先,bm演算法使用的是字尾自包含而z值計算的是字首,另一方面我們還需要找到最長的與字尾相匹配的字首的長度,來修正跳轉值。這裡我們分別來處理這兩個問題。

對於bm演算法中的模式串s,我們可以計算其逆串sr的z值,zi(sr)。例如,對於s="abc***abc",sr="cba***cba",我們可以得到sr的z值表如下圖所示12

3456

789sr

cbax

xxcb

azi(sr)00

0000

300

我們可以用如下方法計算出模式串s的最大包含字尾表rpr(i)。遍歷sr的所有z值,對於滿足n-i-zi(sr)+1>0的i,令rpr(n-i-zi(sr)+1)=zi(sr),對遍歷之後未被填充rpr(i)值的元素,賦值0(如果s從索引0開始,則公式要改動為n-i-zi(sr))。

如下圖,這裡要注意,對於i=7,z7(sr)=3,但是9-7-3+1<=0,所以我們放棄這個值。12

3456

789s

abcx

xxab

crpr(i)00

0000

000

之後,我們可以計算出未修正的好字尾跳轉表。對於rpr(i)=0的元素,goodsuffix'[i]=patlen+n-i,對於rpr(i)!=0的元素,goodsuffix'[i]=n-rpr(i),其中n是模式串最末元素的索引值。如果模式串的首字元從0開始的話,n!=patlen這裡要特別注意。12

3456

789s

abcx

xxab

cgoodsuffix'

1716

1514

1312

11101

另外,我們還需要找到與字尾匹配的最長字首p,用於修正goodsuffix'的跳轉步數。p值在構建zi(sr)的時候可以得到,對於sr中的元素sr[i],如果有i+zi(sr)-1=n,那麼p=zi(sr),如果有多個i滿足該條件,則p等於其中的最大者。上例中對於sr="cba***cba",我們有7+z7(sr)-1=9,所以p=z7(sr)=3。在修正goodsuffix'的跳轉步數時,我們對於n-i>=p的元素goodsuffix'值統一減去p即可得到最終的goodsuffix值。如下圖12

3456

789s

abcx

xxab

cgoodsuffix

1413

1211109

11101

上面雖然列出了4個步驟,但是在實際計算bm的好字尾跳轉表的過程中,除了zi(sr)需要單獨計算之外,其餘三個步驟,可以一次完成。

在使用z值表計算kmp演算法或者是bm演算法的跳轉表的過程中,模式串起始索引要特別注意,如果s[0...n]從0開始,則要對一些地方做修正。因為如果模式串從索引0的位置開始,其最末元素n!=patlen,所以在計算過程中,**用的是模式串的長度,**用的是模式串最末元素的索引,要格外留心。

字串匹配 kmp,bm和sunday演算法

先了解一下kmp的核心,跳轉表 跳轉表是對於模式字串而言,即你需要在目標字串匹配到的內容。例如你想在ababdbccbababcd中匹配到baba,那麼baba就是模式字串。下面介紹跳轉表的實現方法 跳轉表的匹配是匹配前字尾,匹配目標是永遠從第乙個字元開始的字串的字串,例如baba這個字串 匹配目標...

再談RMQ演算法

大家知道rmq演算法就是求乙個區間裡的任意區間的最大值和最小值的高效演算法,因為只需要乙個預處理就可以在o 1 的時間裡算出任意區間的最值。所以當遇到在乙個區間裡求最值之差不超過某乙個數的最長子區間問題就可以利用該方法,當然還需要用到二分演算法。舉個例子 在8,4,2,4,3,2,4,5,2,6這個...

再談貪心演算法

我一直認為貪心演算法可以說是這些演算法中最好理解,最簡單了,因為他比較符合我們人類的心理,符合我們平時的思考模式,幾乎總是選擇最優解,去找最大利益而不去考慮後效性的問題。但是才看書沒幾行一句話就給我噎住了,那句話寫的時我們平時習慣用貪心演算法解決資料規模較大的問題,資料規模較大我想到的是大數乘之類的...