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;
}
=================================================
开发的一种快速字符串匹配算法。当然这里说的字符串
不全指'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;
}
=================================================