KMP演算法概要

2021-10-10 08:28:03 字數 3230 閱讀 7159

前言

經典空間換時間演算法 看了賊久才看懂 網上的部落格文章有很多 但是基本都很複雜 從頭到尾說一遍我是真的沒那精力和耐心 因此這篇只說一下最核心的幾個問題

寫的更多是我的思考的過程 希望能對大家有幫助

請結合jlu資料結構課本食用

kmp這三個老傢伙乙個比乙個頂

我們知道最簡單的字串匹配方法是暴力破解,即將模式串與主串從第一位開始匹配,若不成功則讓模式串從主串第二位開始匹配,一直到模式串末尾與主串末尾重合為止。

不用想也知道這種演算法雖然簡單但是極其耗時

為什麼?

因為在對失敗位置之前的n個字元進行匹配的過程中我們已經獲得了前n個字元的資訊而不對這些資訊進行判斷直接從初始位置的下一位開始匹配是對已知資訊的一種浪費

如何改進?

//舉個栗子

主串: abaaabab(s)

模式串:abab(p)

這時我們已經知道p[0]=p[2] p[1]=p[3]

開始第一次匹配 結果為 s[0]=p[0] s[1]=p[1] s[2]=p[2]s[3]!=p[3]

這時按照爆破演算法第二次應當逐一匹配

主串:   abaaabab(s)

模式串: _abab(p)

即比較s[1]與p[0] s[2]與p[1] s[3]與p[2] s[4]與p[3]

而我們第一次匹配已經知道

s[1]=p[1] s[2]=p[2]

所以第二次匹配如果想要成功

起碼要滿足前提 p[1]=p[0] 且p[2]=p[1]

但是模式串p顯然不滿足這個條件 所以第二次匹配可以直接跳過

這樣一來 通過這次判斷 我們將問題轉化到了p字串本身 減少了s與p的比較次數 從而降低了時間複雜度 而如果我們多次進行這樣的判斷省去中間所有沒有意義的匹配過程那不就是對演算法進行了優化嗎?

假定p串長度為m

對於通常情況來說假設某一次匹配某次匹配後,stst+1…st+j與p0p1…pj匹配而st+j+1與pj+1不等,匹配失敗

通過之前的分析,我們知道此時要比較p本身 如果p0…pj-1不等於p1…pj的話爆破演算法的下一步匹配就可以去掉

然後繼續對p串進行移位 直到找到乙個k 使得p0…pk=pj-k…pj 這時才滿足了判斷的前提 同時根據已經匹配上的部分可以知道

st+j-k…st+j=pj-k…pj=p0…pk也就是說p串的前k位已經可以匹配s串的對應位置了那麼只需要對p串中剩餘的其他位與s串後面的位置進行比較即可

即比較st+j+1…st+j+m-k與pk+1…pm是否相等

推廣一下kmp的思路即為 匹配失敗後通過對模式串本身進行對比計算出模式串與主串已經匹配了的k位 然後從模式串的下一位開始繼續進行下一次匹配

而在上面的過程中 最核心的問題就是計算k的值 k值關係到匹配失敗後下一次匹配的回溯位置 而課本上引入了失敗函式f(i); 其含義為 從p[0]到p[i]的字串中字首和字尾的最長重複長度-1

//舉個栗子

p: abcab

f(i):-

1-1-

101

k值計算依然從一般情況來考慮,假定f(i)=k;

即在前 i 位之中的前k位與後k位相等

要求得f(i+1)

分情況討論

如果pk+1=pi+1 f(i+1)=k+1;

如果pk+1!=pj+1 說明由於第i+1位的加入 使得 p串前 i+1 位中無法滿足前k+1位與最後k+1位相等 因此我們需要尋找前i位中的另乙個滿足字首字尾相等的新字串 並嘗試將該新串的後一位與p串的i+1相比較 確定是否滿足條件 必然有f(i+1)<=f(i) 而如何去找在前i+1位中滿足前j位與後j位相等的那個j呢?

我們將前i位命名為串a,前k為命名為串b 要求的前j位命名為c

由剛才的分析可知 j一定小於等於k 因此串c一定是串b的子串 而c還滿足c與a的後j位相等 而a的後k位又等於串b 所以c也與b的後j位相等

p0…pj=pk-j…pk=pi-j…pi

又p0…pj=pk-j…pk 則f(k)=j

如果不能夠找到滿足條件的j 說明由於第i+1位的加入 使得前i+1位中 必不可能存在前後相等的子串 如果能夠找到 說明前i位中的前j位與後j位相等 那麼此時需要再去判斷 如果成功 那麼便找到了j 因此可以得出f(i+1)=j+1=f(k)+1=f(f(i))+1=f2(i)+1

若串c仍不能滿足最後一位相等,則可再找出乙個新串d 此時可以推出f(i+1)=f3(i)+1

因此可以歸納為

若經過x次運算過程後找到了滿足條件的子串 則

f(i+1)=fx(i)+1

若不存在滿足條件的子串則

f(i+1)=-1輸出結果:

第一行為失敗函式

第二行為匹配成功時的字串首位置

//舉個栗子

#include

#include

#include

using namespace std;

int match

(char mian[

100000

],char re[

100000

],int lenth1,int lenth2,int f[

100000])

else

else}}

if(i<

strlen

(re)

)else

return p;

}int main()

if(re[i]

==re[j+1]

)else

cout<<<

" ";

} cout

match

(mian,re,

strlen

(mian)

,strlen

(re)

,f);

cout

}

資料結構 KMP演算法概要

kmp演算法是一種模式串匹配演算法,即在主串s中找到模式串t第一次出現的首個字元在主串中的位置的一種相對高效的演算法。為什麼說kmp相對高效,這是因為在蠻力 bf 演算法中如果當i j時,不僅僅j 模式串指標 需要回退到第乙個字元的位置,並且主串指標 此處的指標說的是i 也需要回退到第 i j 1 ...

kNN演算法概要

一 演算法概述 1 knn演算法又稱為k近鄰分類 k nearest neighbor classification 演算法。最簡單平凡的分類器也許是那種死記硬背式的分類器,記住所有的訓練資料,對於新的資料則直接和訓練資料匹配,如果存在相同屬性的訓練資料,則直接用它的分類來作為新資料的分類。這種方式...

排序演算法概要

希爾排序 歸併排序 快速排序 堆排序 每次選擇乙個最小 最大 的元素,放到陣列的最前面。過程 在要排序的一組數中,首先遍歷一遍,找到最小的數,和放在第乙個位置的數交換。然後從第二個數開始遍歷,找到最小的數,和放在第二個位置的數交換。就這樣一直迴圈,直到最後只剩下乙個數。由以上過程可以知道,選擇排序演...