zoukankan      html  css  js  c++  java
  • 内核中Boyer-Moore (BM)算法简单注释

    一、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) ", 
    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! ");
    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的字符串就立即退出。
    }
    }
    }
  • 相关阅读:
    POJ 2923 Relocation (状态压缩,01背包)
    HDU 2126 Buy the souvenirs (01背包,输出方案数)
    hdu 2639 Bone Collector II (01背包,求第k优解)
    UVA 562 Dividing coins (01背包)
    POJ 3437 Tree Grafting
    Light OJ 1095 Arrange the Numbers(容斥)
    BZOJ 1560 火星藏宝图(DP)
    POJ 3675 Telescope
    POJ 2986 A Triangle and a Circle
    BZOJ 1040 骑士
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487905.html
Copyright © 2011-2022 走看看