KMP演算法 入門篇

2021-10-23 01:49:15 字數 3228 閱讀 9737

字串匹配問題

給出乙個源字串和乙個目標字串,能否在源串中找到匹配目標串的子串,找到的話就返回首字母在源串的下標

暴力匹配演算法(bf演算法)

相對於烤饃片(kmp)演算法,暴力演算法是最簡單易懂的,就是從前向後遍歷源串的每乙個字母向後檢視以該字母的子串,當且僅當起點字母後面的每個字母都匹配成功則返回該字母下標,否則就繼續遍歷直到源串尾

intbf(

char

* s,

char

* p)

else}if

(j==len2)

return i-j;

return-1

;}

kmp演算法(烤饃片演算法)

對於暴力演算法,我們不難發現,如果匹配到不相同的字元,但是在最後乙個不匹配的字母前已經有多個字元匹配成功,如果只向後移動乙個字元,在前面字元不重複的情況是肯定不會匹配成功,也就是說右移一次繼續判斷的操作是無用功。觀察下圖,可以發現目標串當前不匹配處前面的"abab"前一半等於後一半,因此將目標串的指標向前移動兩個字元,目標串』c』前面的前一半和後一半是匹配的,那麼繼續判斷指標j(指向目標串』a』)和指標i(仍指向源串的』d』),如果所有的匹配都按這樣來,時間複雜度會降低很多。我們把向前移動的次數定義為next陣列,也稱作最大字首字尾匹配長度,下面分析如何求出next陣列

首先引入字首串——即prefix的概念。下面說的字首字尾是指不包含最後乙個字母的串,但是這裡,字首串是和"字首和"類似的概念,即以首字母為起點的所有長度的子串,如下圖是"ababc"的所有字首:

如果我們想優化查詢演算法,顯然需要求出所有字首串的最大字首字尾匹配長度,之所以這裡的字首和字尾不包含尾首字母,是因為這樣就沒了意義——任何字串的最大匹配都是它的長度。那麼"abab"串的最大匹配就是字首"ab"和字尾"ab",即最大匹配為2,因此我們得到上面串的最大長度如下:

但是按第一次舉的栗子,是當』c』不匹配時,才需要字串"ababc"的不含』c』字首串"abab"的最大匹配長度,因此對於每乙個字首串,我們需要的是前乙個字首串的最大匹配長度作為next陣列的值,這樣的理解是對於每乙個字首,當最後乙個字元不匹配時,移動的次數就是除最後乙個字元外的字首串的最大匹配長度。因此,上面的對應值整體下移,然後給next[0]賦值為-1,因為大於0的長度都可能出現,-1可以理解為前面是空串沒有元素,在後面有意想不到的作用

接下來看next陣列如何初始化,首先放出**,根據**研究:

const

int maxn=?;

int next[maxn]

;void

initnext

(char

*p)else k=next[k]

;//遞迴找到前面匹配的狀態或者到達next[0]

}}

假設目標串為"abcabd",下面一步步分析如何求得每個下標的next陣列的值:

j指標是沿著目標串不斷後移的指標,記錄每個字元下標的next,k在前,k的下標剛好記錄當前字元前乙個字首匹配了多少字元,更神奇的是,以 「abcabd"為例,k=2代表字首串"abcab"的最大長度匹配為2,下標為2的字元是』c』,也就是說在最後乙個字元』d』不匹配時,直接將指標j指向下標為k=2的字元,剛好達到了移動的效果。再看k=0時,代表前面前面匹配了0個字元,觀察函式不難發現k=0一定是由k=-1的狀態得到的。假如上面例子接下來是"abcabd?」,』?『為任意字元,下一步我們就是考慮以 「abcabd」 為字首的串,首先看k下標的字元』c』和j下標的字元』d』失配,那麼k=next[2]即k=0,也就是說,回到』c』 字元前乙個位置的匹配狀態,如果還不能匹配,就繼續遞迴,直到k=-1代表不可能再找到匹配,就將p[j+1]更新為k+1,也就是-1+1=0

為什麼說失配時需要回到next[k]的狀態,前面我們已經說了next[k]剛好指向的是該串的字首串的"匹配字首"的下乙個位置(如"ababc"的最大匹配長度為2,那麼p[2]='a』剛好是匹配字首"ab"的下乙個位置),我們上面的過程看作目標串字首字尾的自我匹配,那麼下圖也顯示了失配時的匹配狀態:

不難發現』c』和』d』失配後,剩下的工作就是找已經匹配的字首字尾中子串"ab"的最大匹配長度,由此確定向後移多少位繼續匹配。子串"ab"也就是"abc"的字首串的最大匹配長度,這個我們剛好在一開始就已經求出來,也就是next[2]的值,因為是0,因此就將下面的串移到0的位置繼續和』d』字元比較,這便是遞迴k=next[k]的奧妙

最後,在kmp演算法主體中,大致和暴力匹配演算法相同,只是當匹配或者next陣列為-1時,i繼續向後,匹配的情況不必多說;之所以next陣列為-1時也要繼續向後,是因為next陣列為-1表示當前i需要和目標串前的空串比較,肯定是不相等的,那麼i和j都自增,從i+1和目標串的首部開始繼續比較

const

int maxn=?;

int next[maxn]

;void

initnext

(char

*p)else k=next[k];}

}int

kmp(

char

*s,char

*p)else j=next[j];}

if(j==len2)

return i-j;

return-1

;}

2.b站正月點燈籠的kmp字串匹配演算法1和kmp字串匹配演算法2

3.知乎繁星藍雨的文章——在這裡我真正懂了next陣列遞迴實現的原理

雜湊演算法》入門篇

兩段字串,判斷它們是否相等,樸素解法是乙個乙個的判斷,時間復制度較大。雜湊演算法把字串轉換成整數,這樣時間複雜度從o n 變成了o 1 類似於二進位制,用p進製將字串裝換成整數,為避免重複,一般認為p取131或者1331,使用unsigned long long 就可以,預設對結果模乙個2 64,會...

演算法 入門篇(歐式距離)

計算公式 本文將根據使用者輸入特徵的資料模型,對歐式距離的應用進行簡單的介紹。總思路 根據使用者的歷史登入的輸入特徵,建立 圓形 的資料模型,取落入此圓內的資料,作為正常值,落到圓外部的資料將被判定為風險資料。public boolean doeval double inputfeatures,li...

演算法 入門篇(球面距離)

數學模型 計算公式 l r arc cos cos wa cos wb cos jb ja sin wa sin wb 補充 球面距離在開發中一般用在,地理位置的位移量計算中。本文將根據風控系統中,使用者登入的位移量評估簡單介紹此演算法的使用。一般的,我們再日誌中,可以獲取客戶端的登入ip,根據ip...