1. 程式人生 > >內核中Boyer-Moore (BM)算法簡單註釋

內核中Boyer-Moore (BM)算法簡單註釋

一個 string sea 再次 字符 發現 got n) 生成

一、shift生成 這個算法之前大致看過一下,在grep中再次遇到了該算法,大致想了下它的具體實現,發現shift數組的計算並沒有像KMP中那樣的叠代過程,之後就在網絡上搜索了下這個算法的描述,主要是看shift的生成方法,具體思想描述有不少圖片甚至視頻展示,這裏就不詳細說明了。關於shift的生成有不少偽代碼,加上文字註釋,看起來相當費解,剛好想到內核中有關於該算法的一個實現,所以就看了這個算法在內核中的實現。 總體來說,它分兩種情況,一種是自右向左匹配過程中,第一個字符就匹配失敗,此時不能從pattern中獲得任何有用的信息,只能根據輸入字符串(Text不是pattern)的當前位置字符c,確定該字符在pattern中從右向左第一個出現的位置,和這個位置對齊即可,這種情況下的移位在內核中稱為bad_shift;另一種是pattern中有一個或者一個以上的字符匹配成功,此時可以利用完整的pattern結構來計算出移位信息,這個移位值使用goo_shift表示。 二、代碼註釋
只是為了便於自己之後再翻閱,隨手一個筆記: static unsigned int bm_find(struct ts_config *conf, struct ts_state *state) { struct ts_bm *bm = ts_config_priv(conf); unsigned int i, text_len, consumed = state->offset; const u8 *text; int shift = bm->patlen, bs; for (;;) {//這一層循環是讀入一個block的大小 text_len = conf->get_next_block(consumed, &text, conf, state); if (unlikely(text_len == 0)) break; while (shift < text_len) { DEBUGP("Searching in position %d (%c)\n", shift, text[shift]); for (i = 0; i < bm->patlen; i++) //從右向左匹配,其中i表示距離pattern最後端的距離 if (text[shift-i] != bm->pattern[bm->patlen-1-i]) goto next; /* London calling... */ DEBUGP("found!\n"); return consumed += (shift-(bm->patlen-1)); next: bs = bm->bad_shift[text[shift-i]]; /* Now jumping to... */在該操作中,shift向後移動,shift指向輸入text中嘗試和pattern結尾自右向左匹配的起始位置。 shift = max_t(int, shift-i+bs, shift+bm->good_shift[i]); } consumed += text_len; } return UINT_MAX; } static int subpattern(u8 *pattern, int i, int j, int g) { int x = i+g-1, y = j+g-1, ret = 0; x表示i開始字符串的結尾位置,y表示j開始字符串結尾位置,從右向左開始比較兩個字符串 while(pattern[x--] == pattern[y--]) { if (y < 0) {如果y<0說明前面已經沒有空間,直接返回成功。在該條件下返回,對應pattern:aabaa,(可以以輸入文本:bcbaabaa),入參i=2,j=-1,g=3,x=2,y=-1這種情況。 ret = 1; break; } if (--g == 0) {//g==0表示說找到一個完整的相同匹配,此時需要進一步比較前一個字符是否相等,如果前一個字符相等,則不能作為候選移動項,原因比較明顯,兩者完全相等,第一個後綴沒有匹配成功,第二個由於和第一個完全相等,也不可能匹配成功。對應輸入參數pattern:abcdbcd,i=4,j=1,g=3的情況。 ret = pattern[i-1] != pattern[j-1]; break; } } return ret; } static void compute_prefix_tbl(struct ts_bm *bm) { int i, j, g; for (i = 0; i < ASIZE; i++) bm->bad_shift[i] = bm->patlen; for (i = 0; i < bm->patlen - 1; i++) bm->bad_shift[bm->pattern[i]] = bm->patlen - 1 - i; /* Compute the good shift array, used to match reocurrences * of a subpattern */ bm->good_shift[0] = 1; for (i = 1; i < bm->patlen; i++) bm->good_shift[i] = bm->patlen; for (i = bm->patlen-1, g = 1; i > 0; g++, i--) { for (j = i-1; j >= 1-g ; j--)這裏對於前綴的搜索使用的是最為直觀原始的方法,至少是一種我可以看明白的方法。其中i表示後綴suffix在pattern中下標,g表示從i到pattern結束位置之間的字符串長度,用C語言描述兩個變量的關系為i+g=strlen(pattern)。j從i-1開始,從大到小(也就是對應字符串從右向左)一直遞減到1-g,subpattern函數比較從j開始長度為g和從i開始長度為g的兩個字符串從右向左比較是否滿足subpattern條件。這裏滿足subpattern分兩種情況,一種是兩個字符串完全相等並且前一個字符不等;另一中情況是兩個字符串從右向左只有部分字符串完全相等。 if (subpattern(bm->pattern, i, j, g)) { bm->good_shift[g] = bm->patlen-j-g;//這裏的j+g前面pattern的結束位置,從邏輯上看,這個位置需要和pattern結束位置對齊,所以這個移動位置就是bm->patlen-(j+g) = bm->patlen-j-g。如果bm->good_shift[g]=s,表示說從右向左匹配第g個匹配成功但是第g+1匹配失敗時,string向後移動的距離。 break;//這個break比較關鍵,當尋找到一個匹配之後就跳出循環,也就是從右向左第一個滿足subpattern的字符串就立即退出。 } } }

內核中Boyer-Moore (BM)算法簡單註釋