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的字符串就立即退出。
    }
    }
    }
  • 相关阅读:
    继承(JAVA)
    第一个Java应用
    Java面向对象编程
    学生管理系统(分层开发)
    ComboBox的数据联动
    二进制、八进制、十进制、十六进制之间的转换
    C# using的一些事
    JVM最多支持多少个线程?
    Java日志体系居然这么复杂?——架构篇
    java 架构之路(队列)kafka
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487905.html
Copyright © 2011-2022 走看看