字串匹配演算法很多,boyer-moore演算法也不算是效率最高的演算法,它常用於各種文字編輯器的」查詢」功能(ctrl+f)。
比較經典的字串模式匹配演算法還有:horspool演算法、sunday演算法、kr演算法、ac自動機等。不多說,進入主題。
假定字串為」here is a ****** example」,搜尋詞為」example」。
首先,」字串」與」搜尋詞」頭部對齊,從尾部開始比較。
這是乙個很聰明的想法,因為如果尾部字元不匹配,那麼只要一次比較,就可以知道前7個字元肯定不是要找的結果。
我們看到,」s」與」e」不匹配。這時,」s」就被稱為」壞字元」(bad character),即不匹配的字元。我們還發現,」s」不包含在搜尋詞」example」之中,這意味著可以把搜尋詞直接移到」s」的後一位。
依然從尾部開始比較,發現」p」與」e」不匹配,所以」p」是」壞字元」。但是,」p」包含在搜尋詞」example」之中。所以,將搜尋詞後移兩位,兩個」p」對齊。
如果」壞字元」不包含在搜尋詞之中,則上一次出現位置為 -1。
以」p」為例,它作為」壞字元」,出現在搜尋詞的第6位(從0開始編號),在搜尋詞中的上一次出現位置為4,所以後移 6 - 4 = 2位。再以前面第二步的」s」為例,它出現在第6位,上一次出現位置是 -1(即未出現),則整個搜尋詞後移 6 - (-1) = 7位。
依然從尾部開始比較,」e」與」e」匹配。
比較前面一位,」le」與」le」匹配。
比較前面一位,」ple」與」ple」匹配。
比較前面一位,」mple」與」mple」匹配。我們把這種情況稱為」好字尾」(good suffix),即所有尾部匹配的字串。注意,」mple」、」ple」、」le」、」e」都是好字尾。
比較前一位,發現」i」與」a」不匹配。所以,」i」是」壞字元」。
根據」壞字元規則」,此時搜尋詞應該後移 2 - (-1)= 3 位。問題是,此時有沒有更好的移法?
計算時,位置的取值以」好字尾」的最後乙個字元為準。如果」好字尾」在搜尋詞中沒有重複出現,則它的上一次出現位置為 -1。
所有的」好字尾」(mple、ple、le、e)之中,只有」e」在」example」之中出現兩次,所以後移 6 - 0 = 6位。
可以看到,」壞字元規則」只能移3位,」好字尾規則」可以移6位。所以,boyer-moore演算法的基本思想是,每次後移這兩個規則之中的較大值。
更巧妙的是,這兩個規則的移動位數,只與搜尋詞有關,與原字串無關。因此,可以預先計算生成《壞字元規則表》和《好字尾規則表》。使用時,只要查表比較一下就可以了。
繼續從尾部開始比較,」p」與」e」不匹配,因此」p」是」壞字元」。根據」壞字元規則」,後移 6 - 4 = 2位。
/*
函式:int* makebadchartable(char *,
int)
目的:根據壞字元規則做預處理,建立一張壞字元表
引數:p => 模式串p
plen => 模式串p長度
返回:int* - 壞字元表
*/int* makebadchartable(char *p, int plen)
//初始化壞字元表,256個單元全部初始化為plen,沒有在模式串出現的字元距離為plen。
for(i = 0; i < 256; i++)
//給表中需要賦值的單元賦值,不在模式串中出現的字元就不用再賦值了
//以陣列小標為字元鍵,以值為字元壞字元位置
while(plen != 0)
return badchar;}/*
函式:int* makegoodshifttable(char *,
int)
目的:根據好字尾規則做預處理,建立一張好字尾表
引數:p => 模式串p
plen => 模式串p長度
返回:int* - 好字尾表
*/int* makegoodshifttable(char* p,int plen)
c = *(p + plen - 1);//儲存模式串中最後乙個字元,因為要反覆用到它
*sptr = 1;//以最後乙個字元為邊界時,確定移動1的距離
pptr--;//邊界移動到倒數第二個字元(這句是我自己加上去的,因為我總覺得不加上去會有bug,大家試試「abcdd」的情況,即末尾兩位重複的情況)
while(sptr-- != shift)while(p3 >= p && p2 >= pptr);
*sptr = shift + plen - sptr + p2 - p3;//儲存好字尾表中,以pptr所在字元為邊界時,要移動的位置
/*ps:在這裡我要宣告一句,*sptr = (shift + plen - sptr) + p2 - p3;
大家看被我用括號括起來的部分,如果只需要計算字串移動的距離,那麼括號中的那部分是不需要的。
因為在字串自左向右做匹配的時候,指標是一直向左移的,這裡*sptr儲存的內容,實際是指標要移動
距離,而不是字串移動的距離。我想snort是出於效能上的考慮,才這麼做的。
*/pptr--;//邊界繼續向前移動
}return
shift;}/*
函式:int* bmsearch(char *,
int , char *,
int, int
*,int
*) 目的:判斷文字串t中是否包含模式串p
引數:buf => 文字串t
blen => 文字串t長度
ptrn => 模式串p
plen => 模式串p長度
skip => 壞字元表
shift => 好字尾表
返回:int - 1表示成功(文字串包含模式串),0表示失敗(文字串不包含模式串)。
*/int bmsearch(char *buf, int blen, char *ptrn, int plen, int
*badchar, int
*goodshift)
}skip_stride = badchar[(unsigned char)buf[b_idx]];//根據壞字元規則計算跳躍的距離
shift_stride = goodshift[p_idx];//根據好字尾規則計算跳躍的距離
b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;//取大者
}return
0;}
這個演算法理解起來,比kmp容易多了,但是整體還是很複雜。效率看似不比kmp高多少,但是在實際應用的資料中,效率卻高很多。
最後,概念和**分別來自網際網路,這裡我只是做了乙個比較全面的總結。我想重點在於理解演算法的思路吧。
字串匹配演算法 字串匹配演算法總覽
字串匹配在文字處理裡非常重要,我們採用簡潔的python 把以下演算法一一實現並講解。樸素演算法 algorithm rabin karp 演算法 有限自動機演算法 finite automation knuth morris pratt 演算法 kmp algorithm boyer moore ...
字串匹配演算法
首先引用一下另一篇文章中對字串匹配的介紹 字串匹配指的是從文字中找出給定字串 稱為模式 的乙個或所有出現的位置。本文的演算法一律輸出全部的匹配位 置。模式串在 中用x m 來表示,文字用y n 來,而所有字串都構造自乙個有限集的字母表 其大小為 根 據先給出模式還是先給出文字,字串匹配分為兩類方法 ...
字串匹配演算法
平常操作文字的時候,經常需要操作對字串進行操作。而字串中最重要的一種操作就叫匹配,字串的匹配演算法很多,人們最熟悉的莫過於kmp演算法了。今天就來談一談一些字串匹配演算法。先來說說大名鼎鼎的kmp演算法,這個演算法出現在無數的資料結構與演算法書上面。它的策略很簡單 當模式串第k個字元不匹配主串中第s...