Boyer-Moore模式匹配算法

来源:互联网 发布:手机直播伴奏软件 编辑:程序博客网 时间:2024/05/22 05:35
BM算法是由Robert S. Boyer和J Strother Moore在1977年
开发的一种快速字符串匹配算法。当然这里说的字符串
不全指'abcd'这样的可读字符组成的串,它可以是一片连续的内存。

在这里对该算法做简要描述。

设pat是一个模式串, 我们要在串string里搜索模式串pat是否存在。
设pat_len是pat的长度。设str_len是string的长度。
设pat[j]是pat的第j个元素,j = 0 ~ pat_len - 1。
设string[j]是string的第j个元素,j = 0 ~ str_len - 1。

做两个表:delta1和delta2

delta1的定义如下:
delta1是一个256个元素的int型数组。之所以是256个,是因为
unsigned char型可以表示0~255的整数。设c是一个unsigned char型变量。
则:
delta1[c] = pat_len  若pat里没有c,
否则,
delta1[c] = pat_len - 1 - i;  (i是c在pat中出现的最后位置)

delta2的定义如下:
delta2是一个有pat_len个元素的int型数组。delta2的第j个元素
delta[j]是两个整数的和。即

delta[j] = A + B

A: 是一个最短滑动距离:当元素pat[j]匹配失败时,pat中的元素
   向右滑动以便重新对齐重合string里已经成功匹配的在pat中最后
   pat_len - j - 1个元素的最右面相关子串的距离。
   若无法重新对齐重合, 则A是pat_len - 1。
B: 是pat_len - j - 1

有了delta1和delta2,我们就可以看看BM算法如何在string中的搜索pat。
下面的伪代码就演示了这个搜索过程。摘自Boyer和Moore的论文,做了
一些修改。

         i <--- pat_len - 1
top:     if i > str_len - 1 then return false
         j <--- pat_len - 1
loop:    if j >= 0 and string[i] = pat[j]
         then
              j <--- j - 1
              i <--- i - 1
              goto loop
          if j < 0 then return i + 1
          i <--- i + max(delta1[string[i], delta2[j])
          goto top


文章最后的C语言程序实现了BM算法。
make_skip函数生成delta1;
make_shift函数生成delta2;

编译:
    gcc -std=gnu99 -g -W -Wall -Wextra -o mytest main.c

执行:
    ./mytest
    ABCXXXABC XXXABCXXXABC
    String is:   XXXABCXXXABC
    Pattern is:  ABCXXXABC
    i: 8, slide: 3 skip: 3, shift: 1
    Find out: ABCXXXABC
    ====================================
    ABXYCDEXY ABABXYCDEXYAB
    String is:   ABABXYCDEXYAB
    Pattern is:  ABXYCDEXY
    i: 8, slide: 2 skip: 2, shift: 1
    Find out: ABXYCDEXYAB
    ====================================
    KKKK abKKKKba
    String is:   abKKKKba
    Pattern is:  KKKK
    i: 1, slide: 4 skip: 4, shift: 4
    Find out: KKKKba
    ====================================
    abcxxabc ddddxabcxxabc
    String is:   ddddxabcxxabc
    Pattern is:  abcxxabc
    i: 3, slide: 9 skip: 8, shift: 9
    Find out: abcxxabc
    ====================================
    abcd dcbaacbdbedaadcbacdb
    String is:   dcbaacbdbedaadcbacdb
    Pattern is:  abcd


下面的代码实现BM算法
main.c:
=======================================
// 2012年 02月 24日 星期五 08:54:17 CST
// author: 李小丹(Li Shao Dan) 字 殊恒(shuheng)
// K.I.S.S
// S.P.O.T
// learn BM algorithm


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>



static int *make_skip(unsigned char *, int);
static bool is_prefix(unsigned char *, int, int);
static int len_prefix(unsigned char *, int, int);
static int *make_shift(unsigned char *, int);
static unsigned char *bm_search(unsigned char *, int,
    unsigned char *, int, int *, int *);


int main()
{
    char buf[1024];
    char pat[1024];
    while(scanf("%s %s", pat, buf) == 2) {
        printf("String is:   %s\n", buf);
        printf("Pattern is:  %s\n", pat);

        const int pl = strlen(pat);
        int *skip = make_skip((unsigned char *)pat, pl);
        int *shift = make_shift((unsigned char *)pat, pl);

        char *p;
        if((p = (char *)bm_search((unsigned char *)buf, strlen(buf),
                (unsigned char *)pat, pl, skip, shift))) {
            printf("Find out: %s\n", p);
            printf("====================================\n");
        }
        free(skip);
        free(shift);
    }
    return 0;
}

static int *make_skip(unsigned char *pat, int pl)
{
    int *skip = (int *)calloc(256, sizeof(int));

    for(int i = 0; i < 256; ++i)
        skip[i] = pl;

    const int lp = pl - 1;
    for(int i = 0; i < pl; ++i)
        skip[(int)pat[i]] = lp - i;

    return skip;
}

static bool is_prefix(unsigned char *pat, int pl, int p)
{
    const int ml = pl - p;
    for(int i = 0; i < ml; ++i, ++p) {
        if(pat[i] != pat[p])
            return false;
    }
    return true;
}

static int len_prefix(unsigned char *pat, int pl, int p)
{
    int i = 0;
    const int lp = pl - 1;
    for(; pat[p - i] == pat[lp - i] && i < p; ++i) ;
    return i;
}

static int *make_shift(unsigned char *pat, int pl)
{
    int *shift = (int *)calloc(pl, sizeof(int));

    const int lp = pl - 1;
    int lpp = lp;
    for(int i = lp; i >= 0; --i) {
        if(is_prefix(pat, pl, i + 1))
            lpp = i + 1;
        shift[i] = lpp + lp - i;
    }
    for(int i = 0, len; i < pl; ++i) {
        len = len_prefix(pat, pl, i);
        if(pat[i - len] != pat[lp - len])
            shift[lp - len] = len + lp - i;
    }
    /*printf("==========shift==========\n");
    for(int i = 0; i < pl; ++i)
        printf("%d\n", shift[i]);
    printf("==========shift==========\n");*/
    return shift;
}

static unsigned char *bm_search(unsigned char *buf, int len,
    unsigned char *pat, int pl, int *skip, int *shift)
{
    if(pl > len) return 0;

    const int lp = pl - 1;
    for(int i = lp, j, sld; i < len; i += sld) {
        j = lp;
        for(; j >= 0 && buf[i] == pat[j]; --i, --j) ;
        if(j < 0) return &buf[i+1];

        int x = skip[(int)buf[i]];
        int y = shift[j];
        sld = x > y ? x : y;

        printf("i: %d, slide: %d skip: %d, shift: %d\n", i, sld, x, y);
    }
    return 0;
}
=================================================


原创粉丝点击