-----------------------
引言:在文字編輯中,我們經常要在一段文字中某個特定的位置找出 某個特定的字元或模式。
由此,便產生了字串的匹配問題。
本文由簡單的字串匹配演算法開始,經rabin-karp演算法,最後到kmp演算法,教你從頭到尾徹底理解kmp演算法。
來看演算法導論一書上關於此字串問題的定義:
假設文字是乙個長度為n的陣列t[1...n],模式是乙個長度為m<=n的陣列p[1....m]。
進一步假設p和t的元素都是屬於有限字母表σ.中的字元。
依據上圖,再來解釋下字串匹配問題。目標是找出所有在文字t=abcabaabcaabac中的模式p=abaa所有出現。
該模式僅在文字中出現了一次,在位移s=3處。位移s=3是有效位移。
一、簡單的字串匹配演算法
簡單的字串匹配演算法用乙個迴圈來找出所有有效位移,
該迴圈對n-m+1個可能的每乙個s值檢查條件p[1....m]=t[s+1....s+m]。
*****-string-matcher(t, p)
1 n ← length[t]
2 m ← length[p]
3 for s ← 0 to n - m
4 do if p[1 ‥ m] = t[s + 1 ‥ s + m]
//對n-m+1個可能的位移s中的每乙個值,比較相應的字元的迴圈必須執行m次。
5 then print "pattern occurs with shift" s
簡單字串匹配演算法,上圖針對文字t=acaabc 和模式p=aab。
上述第4行**,n-m+1個可能的位移s中的每乙個值,比較相應的字元的迴圈必須執行m次。
所以,在最壞情況下,此簡單模式匹配演算法的執行時間為o((n-m+1)m)。
--------------------------------
下面我再來舉個具體例子,並給出一具體執行程式:
對於目的字串target是banananobano,要匹配的字串pattern是nano,的情況,
下面是匹配過程,原理很簡單,只要先和target字串的第乙個字元比較,
如果相同就比較下乙個,如果不同就把pattern右移一下,
之後再從pattern的每乙個字元比較,這個演算法的執行過程如下圖。
//index表示的每n次匹配的情形。
#include
#include
using namespace std;
int match(const string& target,const string& pattern)
else
}if(pattern_index == pattern_length)
else
}int main()
if(pattern[i]==pattern[index+1])
else
}for(i=0;i
執行結果為:
-1-100
1-101
2press any key to continue
-------------------------------------
2、kmp演算法
有了覆蓋函式,那麼實現kmp演算法就是很簡單的了,我們的原則還是從左向右匹配,但是當失配發生時,我們不用把target_index向回移動,target_index前面已經匹配過的部分在pattern自身就能體現出來,只要動pattern_index就可以了。
當發生在j長度失配時,只要把pattern向右移動j-overlay(j)長度就可以了。
如果失配時pattern_index==0,相當於pattern第乙個字元就不匹配,
這時就應該把target_index加1,向右移動1位就可以了。
ok,下圖就是kmp演算法的過程(紅色即是採用kmp演算法的執行過程):
ok,最後給出kmp演算法實現的c++**:
#include
#include
#include
using namespace std;
int kmp_find(const string& target,const string& pattern)
if(pattern[index+1]==pattern[i])
else
}//match algorithm start
int pattern_index = 0;
int target_index = 0;
while(pattern_indexint main()
{string source = " annbcdanacadsannannabnna";
string pattern = " annacanna";
cout<
三、kmp演算法的**
kmp如此精巧,那麼它是怎麼來的呢,為什麼要三個人合力才能想出來。其實就算沒有kmp演算法,人們在字元匹配中也能找到相同高效的演算法。這種演算法,最終相當於kmp演算法,只是這種演算法的出發點不是覆蓋函式,不是直接從匹配的內在原理出發,而使用此方法的計算的覆蓋函式過程式複雜且不易被理解,但是一但找到這個覆蓋函式,那以後使用同一pattern匹配時的效率就和kmp一樣了,其實這種演算法找到的函式不應叫做覆蓋函式,因為在尋找過程中根本沒有考慮是否覆蓋的問題。
說了這麼半天那麼這種方法是什麼呢,這種方法是就大名鼎鼎的確定的有限自動機(deterministic finite state automaton dfa),dfa可識別的文法是3型文法,又叫正規文法或是正則文法,既然可以識別正則文法,那麼識別確定的字串肯定不是問題(確定字串是正則式的乙個子集)。對於如何構造dfa,是有乙個完整的演算法,這裡不做介紹了。在識別確定的字串時使用dfa實在是大材小用,dfa可以識別更加通用的正規表示式,而用通用的構建dfa的方法來識別確定的字串,那這個overhead就顯得太大了。
kmp演算法的可貴之處是從字元匹配的問題本身特點出發,巧妙使用覆蓋函式這一表徵pattern自身特點的這一概念來快速直接生成識別字串的dfa,因此對於kmp這種演算法,理解這種演算法高中數學就可以了,但是如果想從無到有設計出這種演算法是要求有比較深的數學功底的。
KMP演算法詳解
模式匹配的kmp演算法詳解 這種由d.e.knuth,j.h.morris和v.r.pratt同時發現的改進的模式匹配演算法簡稱為kmp演算法。大概學過資訊學的都知道,是個比較難理解的演算法,今天特把它搞個徹徹底底明明白白。注意到這是乙個改進的演算法,所以有必要把原來的模式匹配演算法拿出來,其實理解...
KMP演算法詳解
kmp演算法即knuth morris pratt演算法,是模式匹配的一種改進演算法,因為是名字中三人同時發現的,所以稱為kmp演算法。因為偶然接觸到有關kmp的問題,所以上網查了一下next陣列和 nextval陣列的求法,卻沒有找到,只有在csdn的資料檔案裡找到了next陣列的簡單求法 根據書...
KMP演算法詳解
相信很多人 包括自己 初識kmp演算法的時候始終是丈二和尚摸不著頭腦,要麼完全不知所云,要麼看不懂書上的解釋,要麼自己覺得好像心裡了解kmp演算法的意思,卻說不出個究竟,所謂知其然不知其所以然是也。經過七八個小時地仔細研究,終於感覺自己能說出其所以然了,又覺得資料結構書上寫得過於簡潔,不易於初學者接...