-------------Update 6.9 BUG FIX-------------------------------
init good suffix
中應(yīng)該是到plen-1
而不是plen-2
.(見下面代碼)leetcode/strStr測試通過
寫在最前邊,BM要是會了,相信KMP就是個渣渣.
--------------Update 6.9(實在沒空更新blog了)---------------
本文可以作為BM的deep dive,但你需要先去了解一下BM甚至自己動手試著實現(xiàn)一下测柠,帶著幾個問題來看比較好.
目前實現(xiàn)了bm和暴力设联。
增加了bm算法的DBG和一些test case。
先上代碼地址:
<a href=https://github.com/exctPuzzles/exctpuzzs/>我的git項目exctPuzzles</a>
(在solution/string)下林艘,可以直接make
也可以make test-bm
.有自己設(shè)置的一些benchmark,非常歡迎指出bug,一起學(xué)習(xí)一起進(jìn)步! :)
這里簡要概括一下吧,希望有些同學(xué)能獲得些幫助.
主要講一下BM的好后綴規(guī)則.(壞字符和整體算法同學(xué)們自己去百度/google,看完一篇再來我這看).
理解
bm的好后綴規(guī)則理解起來十分clear峰尝,但是實現(xiàn)起來卻非常confusing.
主要有3個case:
>case 1 - the 'suffix' of the good_suffix appear at the **very** beginning.
>case 2 - the 'whole' good_suffix appear before (no need to be at the begginning)
>case 3 - 'none' of the above.then move over the whole length.
這些一開始不是很好理解,最好多問幾個為什么,比如:
- 為什么部分好后綴匹配的情況收恢,一定只能匹配出現(xiàn)在模式串頭部的呢武学?
反證 - 假如匹配了一個部分好后綴,如:
'd' mismatch, then 'abc' is the *good suffix*
xxxxxabc
cbbcdabc
^
這時我應(yīng)該怎么移動呢祭往? 假如移動使部分好后綴'bc'匹配(此時bc不在串頭),那么顯然火窒,原串中的'a'將不匹配,如下:
'a' mismatch, this move is bad.
xxxxxabc
cbbcdabc
^
所以如果想要移動到一個非串頭的位置硼补,必須要出現(xiàn)整個好后綴,否則此次移動是失敗的.(原諒我語文沒學(xué)好)
- 還有一個我碰到的理解困難就是為什么case 3移動了整個模式串的長度而不是像壞字符規(guī)則,只移動1呢熏矿?
也是反證 - 假如出現(xiàn)了這樣的情況已骇,case1和case2都沒有匹配,但神奇地票编,最終在不到整個串長度的移動下找到了子串褪储,如:
########abc#############
@@@@@@@@abc
we have good suffix 'abc', but no case 1 or case 2.
it suggest move the entire length.
好后綴規(guī)則讓我們移動這么多(1),但老子覺得這不靠譜慧域,老子覺得有可能在前面就出現(xiàn)子串(2)乱豆。這樣你就會發(fā)現(xiàn)^
標(biāo)記出來的幾個字符一定會相等,那么這種情況正好是case 2,自相矛盾.(原諒我的語文)
########abc#############
@@@@@@@@abc
@@@@@@@@abc (1)好后綴規(guī)則建議
@@@@@@@@abc (2)老子的建議
^^^
實現(xiàn)
再來就是實現(xiàn)上的一些障礙了吊趾,我們最終要得到一個與模式串等長的數(shù)組buf[]
宛裕,buf[i]
指出了若這個下標(biāo)的位置出現(xiàn)了不匹配,需要往后移動多少個距離.
好后綴規(guī)則主要是利用了一個長度與模式串相等的輔助數(shù)組suffix[]
论泛,存放以相應(yīng)下標(biāo)結(jié)尾的原模式串的后綴與原模式串的共同后綴長度.
suffix[i] = the common suffix length of [0..i] & [0..L-1]
(suppose the length of pattern is L)
然后呢
我們可以驚奇地發(fā)現(xiàn),
若`suffix[i]!=0`揩尸,則正好是某個好后綴的整個匹配;(case 2)
若`suffix[i] == i+1`則正好表示某個好后綴或某個好后綴的一部分正好出現(xiàn)在串的開頭.(case 1)。
另外屁奏,需要十分注意的是一個suffix[i]可能能夠更新多個buf[j] (j=???可能是多個),這里如果處理得稍有不慎就會造成復(fù)雜度太高或者錯誤.
最后的最后就是需要注意buf[]的值與suffix[]的i相互轉(zhuǎn)換問題了,相信這個大部分人細(xì)心點都能搞定.
(實現(xiàn)這部分的思路改天心情好再更上來)
這里貼一下代碼片段(有bug請盡情噴我)
/*
* @buf - buf[i] if the i-th elem mismatch, (pattern[i+1..$] is good-suffix), move to @buf[i] location to align with the cur pi_src
*/
void init_good_suffix_rule(const char *pattern, int *suff_i_len, int *buf){
/*****prepare suff_i_len******/
int i, j, plen = strlen(pattern);
//int tmp[MAX_KEYWORD_LEN];
suff_i_len[plen - 1] = plen;
i = plen - 2;
while(i >= 0){
j = 0;
while(*(pattern + plen - 1 - j) == *(pattern + i - j)){
j++;
}
suff_i_len[i] = j;
i--;
}
#ifdef DBG_BM_2
printf("suffix length array\n");
for(int i = 0; i < plen; i++){
printf("%d ", suff_i_len[i]);
}
printf("\n");
printf("===================\n");
#endif
/*******prepare suff_i_len done********/
/*******get the buf value for good suffix**/
/*
* case 1: the 'suffix' of the good_suffix appear at the beginning.
- why beginning ? if not, it must match the whole good_suffix & it'll be found at the case_2.
- why 'suffix' but not any part of it ? ==>if it's any part :(a. the prefix of it - if this case work, it'll also be found at the case_2 below
b.any part of it start at the middle - same as above.)
case 2: the 'whole' good_suffix appear before (no need to be at the begginning)
case 3: none of the suffix of good_suffix or the good_suffix itself show at the string.
*/
buf[plen - 1] = 1;//good_suffix's length == 0.
for(i = 0; i < plen - 1; i++)
buf[i] = plen;
int x;
int start_point = 0;
for(i = plen - 2; i >= 0; i--){//start from the longest part-one, because here we choose the least move length.(avoid missing a possible ok-search)
if(suff_i_len[i] == i + 1){
x = plen - suff_i_len[i];//x <--> 0
for(j = start_point; j <= x - 1; j++){//avoid the repeated--------it's a little confusing here. I'll make some explanation.
buf[j] = x;//////////////
}
start_point = x;
}
}//case_1 done
//for(i = 0; i < plen - 2; i++){ BUG_FIX_6.9
for(i = 0; i < plen - 1; i++){
if(suff_i_len[i]){
x = plen - suff_i_len[i]; //x <--> i - suff_i_len[i] + 1
buf[x-1] = x - (i - suff_i_len[i] + 1) < buf[x-1] ? x - (i - suff_i_len[i] + 1) : buf[x-1];
}
}//case_2 done
/*******done*********/
}
打開DBG_BM_0
的一些結(jié)果:
root@vm1:/mnt/win/exctpuzzs/solutions/string# ./test-bm
=======test 0 : 111111kabcbsabc, 1kabcbsabc======
====0====
111111kabcbsabc (0x401021)
1kabcbsabc
====1====
111111kabcbsabc (0x401026)
1kabcbsabc
found! 5
=======test 1 : a b b a d a b a c b m b p b a c , b a b a c======
====0====
a b b a d a b a c b m b p b a c (0x401030)
b a b a c
====1====
a b b a d a b a c b m b p b a c (0x401031)
b a b a c
====2====
a b b a d a b a c b m b p b a c (0x401032)
b a b a c
====3====
a b b a d a b a c b m b p b a c (0x401034)
b a b a c
====4====
a b b a d a b a c b m b p b a c (0x401038)
b a b a c
====5====
a b b a d a b a c b m b p b a c (0x401041)
b a b a c
====6====
a b b a d a b a c b m b p b a c (0x401042)
b a b a c
====7====
a b b a d a b a c b m b p b a c (0x401046)
b a b a c
not found!
====8====
a b b a d a b a c b m b p b a c (0x40104f)
b a b a c
=======test 2 : a b w u t u q n w a b c d p m n a b c d, n w a b c d p m n a b c d======
====0====
a b w u t u q n w a b c d p m n a b c d (0x401068)
n w a b c d p m n a b c d
====1====
a b w u t u q n w a b c d p m n a b c d (0x401076)
n w a b c d p m n a b c d
found! 14
=======test 3 : a b w u t u q m n a b c d m n p q x, a b c d p m n a b c d======
====0====
a b w u t u q m n a b c d m n p q x (0x40108c)
a b c d p m n a b c d
====1====
a b w u t u q m n a b c d m n p q x (0x401090)
a b c d p m n a b c d
not found!
====2====
a b w u t u q m n a b c d m n p q x (0x40109e)
a b c d p m n a b c d
----------------Original------------------------------
最近復(fù)習(xí)面試筆試的一些算法岩榆,從字符串算法開始吧。
首先遇到了第一個比較難的問題坟瓢,子串搜索勇边,即實現(xiàn)一個
const str* strstr(const char* text, const char*pattern)
從一個給定的字符串中搜索一個給定的串(就像Ctrl + F一樣).
暴力不多說.
boyer-moore算法最大的特點是corner case非常難以想象,這可能也是一個訓(xùn)練思維方式的過程.