KMP 演算法(個人理解)

2021-06-07 22:07:01 字數 3429 閱讀 4247

相信大家看了 matrix67 的講解,一定已經知道了 kmp 演算法是怎麼回事,怎麼操作的,為什麼時間複雜度不高

這裡,我主要是分享我對 kmp 的理解

kmp 的精髓是什麼?

這個東西,各自有個字的理解,很多人都覺得是避免了重複匹配,而我的理解是預處理

為什麼是預處理?

你看看 kmp 的執行過程:

假定現在我們有 a 串 :abababacba 我們還有 b 串:babac

進行匹配的時候:

1 2 3 4 5 6 7 89 10

a :a b a b a b a cb a

b :b a b a c

12 3 4 5 6 7 8 9 10

a :a

b a b a b a c b a

b  :  b a b a c

現在,我們發現,a b 串對應的字元相等了,好了,那麼接下來我們怎麼做呢?當然是同時把 a b 串的檢查位子向前移一位,來判斷接下來的字元是不是相等,那麼,我們有了下面的狀態:

1 2 3 4 5 6 7 8 9 10

a :a

ba b a b a c b a

b :   b

a b a c

咦,我們發現,他們還是匹配的,那麼,接下來該怎麼做呢?那就再把檢查位子往前移,同時,我們也不難這樣想,要是移到了b 或者 a 的最後乙個字元了怎麼辦?這就簡單了嘛,要是移動到了 b 串的最後乙個位子,那麼,我們肯定是找到了 b 在 a中的乙個子串了。要是移動到了 a 串的最後乙個位子,那麼,我們已經把 a 串中所有位子找完了,接下來該怎麼做呢?找完了就找完了啊,還能怎麼做?好了,這些都是細節問題,我們先不談,先看看接下來我們按照上面講的操作,得到了什麼樣的狀態:

1 2 34 5 6 7 8 9 10

a :aba

b a b a c b a

b :   b

ab a c

1 2 3 45 6 7 8 9 10

a :a

ba b a b a c b a

b :   b

a b a c

1 2 3 4 5 6 7 8 9 10

a :a

ba b a

b a c b a

b :    b

a b ac

我們發現,現在 b 串和 a 串不能匹配了,這時該怎麼辦?

注意了,這就是普通匹配與 kmp 匹配的最大的不同,也是kmp 為什麼高效的最重要原因。一般的,我們普通的匹配,要是遇到了這種情況,我們直接放棄了從 a 串的第 2 個位子開始匹配,轉而以 a 串的第 3 個位子為起始檢查位子,以b 串的第 1 個位子為起始檢查位子,繼續再和 b 串乙個字元乙個字元的匹配,直到匹配完成了為止。這些東西,我在沒有學習kmp 之前就是這麼幹的,那麼,傳說中的 kmp 演算法是怎麼幹的呢?

kmp 是這樣幹的,我們現在不是不能匹配了嗎,我們把 b 串的檢查位子後移,移到什麼程度?假設移到了 pos 這個位子,那麼,b 串中pos 這個位子一定要能夠與 a 串中當前的檢查位子相匹配。

好了,現在,我們來移動 b 串的當前檢查位子:得到了下面的狀態:

1 2 3 4 5 6 7 8 9 10

a :a

ba b a

b a c b a

b :          bab

ac好了,接下來,我們只要重複的進行上面的操作,就能夠成功的在掃了一遍 a 串後,在 a 串中找到乙個子串,它和 b 串匹配

但是,新的問題出來了,就是,當我們遇到 b串的當前檢查位子和 a 串的當前檢查位子不同的時候,我們不是要移動 b 串的檢查位子嗎?怎麼移?一位一位的移?naïve!這樣不就是退化成了普通的匹配了嗎?那我們要怎麼移動呢?

我們再來看看剛才這種狀態:

1 2 3 4 5 6 7 8 9 10

a :a

ba b a

b a c b a

b :   b

a b ac

我們移動 b 串的檢查位子到4,發現什麼規律了嗎?當然,這樣多半不會發現什麼規律,這樣吧,我們做個約定,假設現在的檢查位子在 5 ,我們判斷下乙個位子是否相匹配,相匹配的話,檢查位子再向前移動一位,也就是說,在檢查位子(包括檢查位子)以前,a 串和 b 串是相互匹配的,那麼,遇到上面這種情況,我們要移動 b 串的檢查位子到 3

發現什麼了嗎?

b 串的前 k 個和後 k 個是匹配的!為什麼?這就是a=b c=b 能夠推出來 a=c 一樣的。b 串的後 k 個能夠和a 串當前檢查位子的後k 個字元相匹配,而移動了 b 串的檢查位子後,b串的前 k 個移動到了a 串檢查位子的後k 個位子上,他們是匹配的,這樣,b 串檢查位子處的後 k 個字元和 b串的前 k 個字元相匹配就不難理解了。

問題就簡化成了,我們要求出 b 串每個位子不能匹配的時候能夠退到哪個位子?為了是演算法的時間複雜度更高效,這個位子必須是距離當前不能匹配位子最近

注意這裡:「b 串當前位子的前 k 個字元和後 k 個字元相匹配」,其實,這就轉化成了b 串和自己匹配了。

我們設乙個陣列,記錄每個位子能退到的最近的點,這裡,一般我們字串的開始位子是0,那麼,next[0]=-1,這樣就保證了極端情況

但是,我們的問題還是沒有解決。怎麼找出每個位子的前面的位子,也就是我們的 next[i]?

首先,我們在找 i 這個位子的時候,i 前面的位子的 next 肯定是已經找出來了,怎麼利用前面的 next 來輔助我們找到現在這個位子的 next 呢?如果我們在注意觀察,不難發現,假設現在我們要找的 next[i] ,那麼,如果 b[next[i-1]+1]==b[i] ,那我們的 next[i] 肯定是等於 next[i-1] ,要是 b[next[i-1]]!=b[i] 呢?我們怎麼處理?顯然,這仍然和next[i-1] 有關,為什麼?因為 i-1 之後是i ,就這麼簡單,既然 next[i-1] 不能滿足條件,我們就看 next[i-1] 的 next 滿不滿足條件,即:b[next[next[i-1]]+1]==b[i] 嗎?如果相等,我們找到了,如果不相等,那就繼續找下乙個 next ,直到相等,或者 next 指向了-1

那麼,我們不難寫出如下的**:

是不是很精簡?

剛說了,找 b 串的next 其實就是乙個 b 串自己與自己匹配的過程,這一過程讀者一定要仔細理解,因為 kmp 的經典之處就在這裡:先自己跟自己匹配,在和別人匹配。自己跟自己匹配是為了預處理,這樣在和別人匹配的時候才能夠高效的實現,下面,我們看看和別人匹配的**:

好了,到這裡,我們已經對 kmp 進行了一次分析,不難發現,真正的精髓就在自己和自己匹配找出 next 陣列上了,下面獻上完整的匹配過程**:

KMP演算法的個人理解

自學了一段時間,剛剛準備轉行做軟體開發,面試過程中被指出計算機基礎知識薄弱。因為是非科班出生,確實有些計算機方面的基礎沒有學過,也開始惡補這些方面的東西。最近在學習資料結構與演算法過程中,學到kmp演算法,甚是難解。看了阮一峰的網路日誌後才慢慢理解,但也發現其中的瑕疵,在此也順帶指出,至於對或不對,...

KMP演算法的個人理解

kmp演算法是一種改進的字串匹配演算法,由d.e.knuth,j.h.morris和v.r.pratt同時發現,因此人們稱它為克努特 莫里斯 普拉特操作 簡稱kmp演算法 kmp演算法的關鍵是利用匹配失敗後的資訊,儘量減少模式串與主串的匹配次數以達到快速匹配的目的。kmp演算法的核心內容是乙個部分匹...

KMP演算法個人理解總結

關鍵點 公共前字尾字串 1.首先按照bf比較後,出現的那個不匹配的字元往前找,比如圖中是ab。每次要取最長公共前字尾字串,比如圖中最長的是ab。2.然後,把最長字首移到最長字尾的位置,再從最長字尾開始比較。這是按照移動模式串 子串 的思維來的。實際上操作是這樣 每次開始比較的編號,等於最大公共前字尾...