6、教你從頭到尾徹底理解kmp演算法
引言:在文字編輯中,我們經常要在一段文字中某個特定的位置找出 某個特定的字元或模式。
由此,便產生了字串的匹配問題。
本文由簡單的字串匹配演算法開始,經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這種演算法,理解這種演算法高中數學就可以了,但是如果想從無到有設計出這種演算法是要求有比較深的數學功底的。
ok,完。
從頭到尾寫SQL 一
大二的時候學長說,要學東西真的要從頭到尾寫到部落格上,現在作為大五老學渣來看,真的好有道理。那就從這個貼開始吧,寫乙個sql的系列。現在來看,怎樣能把計算機內的各種資料儲存在一起並且將之分類呢?資料庫提供了很好的解決辦法,廣義上資料庫就是存放資料的倉庫,便於我們查詢歸納。當資料越來越多時,你會發現使...
從頭到尾寫SQL 三
之前操作的都是資料庫的架構,把架子搭好後便可以填充資料了,運算元據,無非增刪改查 insert into person fname,fage values tom 18 向person表中插入一條記錄,fname,fage分別為tom,18 insert 語句中字段的順序是可以任意的,並且不需要將所...
從頭到尾列印鍊錶
輸入乙個鍊錶的頭節點,從尾到頭反過來返回每個節點的值 用陣列返回 題目描述了乙個先進後出的結構,很容易想到棧,進而想到遞迴,遞迴也可以實現先進後出,只要將本節點的操作置於遞迴之後即可。棧 definition for singly linked list.public class listnode ...