內核中Boyer-Moore (BM)算法簡單註釋
阿新 • • 發佈:2019-03-07
一個 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)算法簡單註釋