1.http://blog.csdn.net/joylnwang/article/details/6785743
2.http://www.cnblogs.com/dsky/archive/2012/05/04/2483190.html
3.http://blog.chinaunix.net/uid-23390992-id-3320412.html
一. 1977年,Robert S.Boyer和J Strother Moore提出了另一种在O(n)时间复杂度内,完成字符串匹配的算法,其在绝大多数场合的性能表现,比KMP算法还要出色,下面我们就来详细了解一下这一出色的单模式匹配算法,在此之前推荐读者读一下我的另一篇文章《KMP算法详解》,对于透彻理解BM算法大有裨益。
在讲解Boyer-Moore算法之前,我们还是要提一提KMP算法的老例子,当模式串与目标串匹配至如下位置时:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
b | a | b | c | b | a | b | c | a | b | c | a | a | b | c | a | b | c | a | b | c | a | c | a | b | c |
a | b | c | a | b | c | a | c | a | b |
BM算法之所以能够在单模式匹配中有更加出色的表现,主要是其使用了两个跳转表,一个是坏字符表(论文中称为delta1),一个是好后缀表(论文中称为delta2),下面我们以BM算法对目标串的一次匹配操作,来讲解这两个表的具体跳转策略,这里模式串为"AT-THAT",目标串为"WHICH-FINALLY-HALTS.--AT-THAT-POINT"。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | |
W | H | I | C | H | - | F | I | N | A | L | L | Y | - | H | A | L | T | S | . | - | - | A | T | - | T | H | A | T | - | P | O | I | N | T | |
1 | A | T | - | T | H | A | T | ||||||||||||||||||||||||||||
2 | A | T | - | T | H | A | T | ||||||||||||||||||||||||||||
3 | A | T | - | T | H | A | T | ||||||||||||||||||||||||||||
4 | A | T | - | T | H | A | T | ||||||||||||||||||||||||||||
5 | A | T | - | T | H | A | T |
第一次,pattern[1]与target[1]对齐,从pattern[7]向前依次与target执行比较,但是第1次比较就发现,target[7]='F',而'F'不是pattern串中的字符,所以target中包含target[7]的任何子串都不可能与pattern匹配,此时我们可以直接将pattern串滑动到target[7]之后,让pattern[1]与target[8]对齐,然后再由target[14]依次向前执行比较。
第二次,target[14]='-',虽然'-'是模式串中的字符,但是如果要target串中包含target[14]的字串与pattern串匹配,则至少target[14]需与pattern中最后一个'-'对齐。而pattern中只有一个'-'pattern[3],所以将target[14],与pattern[3]对齐,然后再由target[18]向前依次执行比较。
第三次,虽然target[18]=pattern[7]='T',但是target[17]='L','L'不是pattern中的字符,所以包含target[17]的任何字串都不可能与pattern匹配,所以pattern[1]直接与target[18]对齐再执行匹配。
第四次,target[23...24]=pattern[6...7],target[22]!=pattern[5],我们注意到,pattern[6...7]=pattern[1...2]所以pattern[1...2]也是模式串的一个自包含后缀(下文详述),所以我们可以令pattern[1]与target[23]对齐再向后执行匹配,此时我们就发现了满足条件的匹配串target[23...29]。
该示例使用到了BM算法中的所有跳转优化,大幅加速了模式串的向后滑动过程,实现了模式的快速匹配,其中第1,2,3次滑动使用的是算法中的坏字符移动规则,第4次滑动使用的是好后缀移动规则,那么什么是所谓的坏字符和好后缀规则呢。
所谓的坏字符移动规则,就是一个表,其以输入字符集的所有字符作为索引,每个输入字符c对应着一个值,表示如果目标串的当前位置字符c与模式串匹配失败,那么目标串当前位置应该可以向前滑动的步数。假设字符集为"ABCDEFGHIJKLMNOPQRSTUVWXYZ-",那么他对应模式串"AT-THAT"的坏字符表为。
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | - | |
delta1 | 1 | 7 | 7 | 7 | 7 | 7 | 7 | 2 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 0 | 7 | 7 | 7 | 7 | 7 | 7 | 4 |
坏字符表的定义为,对于输入字符集合中的字符c,如果c不在模式串中,则delta1[c]= patlen(模式串的长度),如果c在模式串中,则delta1[c]=j-i,其中,j是模式串最末元素的索引值,i是字符c在模式串中最右出现的位置(这里与Boyer-Morre两人的论文略有差别,主要是因为BM的论文中,字符串的索引从1开始,其最末元素的索引值,就等于模式串的长度,而在实际计算模式串中含有字符的坏字符滑动值时,使用到的是模式串最末元素的索引值,这个值与模式串的长度不一定相等)。下面就是用于生成坏字符表的代码,为了简单起见,这里没有使用字典结构,而是假设输入的字符只能是A-Z,然后将这26个字符映射到一个数组中。
所谓的好后缀移动规则,是BM算法的核心部分,下面详细说明。在KMP算法中,我们知道了所谓的前缀自包含问题,也就是模式串的前缀也可能是模式串的非前缀子串。在BM算法中,有一个与其非常相似的概念,叫后缀自包含。对于pattern[1...j],存在长度为k的子串,满足pattern[m+1...m+k]=pattern[j-k+1...j],其中k
我们定义数组pre[],与pattern中的元素一一对应,对于pattern中的元素,pattern[i],pre[i]是使得pattern[k+1...j-i]=pattern[i+1...j],且pattern[k]!=pattern[i]的k的最大值,如果不存在这样的k,pre[i]=patlen。对于对于模式串的后缀k pattern[j-k+1...j],满足条件的包含后缀可能不止一个,这里我们需要关注所有满足条件的pattern[m+1...m+k]中,满足pattern[m] != pattern[j-k]的m的最大值。对于上例的模式串,其后缀3 pattern[12...14],其包含后缀有pattern[8...10],pattern[4...6],pattern[1...3],在这3个包含后缀中,pattern[7]=pattern[11],所以pattern[8...10]不是我们想要的包含后缀。pattern[0] != pattern[11](这里面我们假设pattern[0]不等于任何可输入字符),pattern[3]!=pattern[11],在这两个备选子串中,pattern[4...6]的m值(3)大于pattern[1...3]的m值(0),所以pattern[4...6]就是我们需要的pre值。对于为什么要满足pattern[m]!=pattern[j-k],请参考我的《KMP算法详解》一文中对于next[j]与f(j)不同之处的解释,以及本文后面算法正确性方面的说明。
现在我们发现了pattern[12...14]在模式串中的包含后缀pattern[4...6],此时如果我们发现目标串target[n]与模式串pattern[11]比较失败,我们就直接可以将pattern[3]与target[n]对齐,然后再从target[n+11]处向前依次与模式串进行匹配。目标串当前位置的跳转距离goods[i]=j-pre[i]。
这里我们需要解释一下如此大幅跳转的正确性。还是以上述模式串为例,当target[n]与pattern[11]匹配失败时,我们需要找到一个适当的位置,令target[n+1...n+3]与pattern[k+1...k+3]相同,才有可能找到匹配结果,这里target[n+1...n+3]=pattern[12...14]。根据pre[i]的定义,只有当k=3时,才能保证pattern[12...14] = pattern[4...6],对于任何k>3都有pattern[12...14] != pattern[k+1...k+3],因为如果存在k>3使得pattern[12...14] = pattern[k+1...k+3],那么pre[11]必然大于3。所以这一对齐方式不会漏过中间可能的匹配。
这里读者可能会有疑问,你说的实际是错的,对于k=7,有target[n+1...n+3]=pattern[8...10],为什么不让target[n]与pattern[7]对齐,然后从target[n+7]位置开始依次向前比较呢?这个问题和KMP算法中next[j]和f(j)的不同之处一样。虽然有pattern[8...10]=pattern[12...14],但是pattern[7]=pattern[11]。因为target[n] != target[11],所以target[n]!=pattern[7]所以将target[n]与pattern[7]对齐所执行匹配尝试必然失败,所以target[n]可以直接跳过pattern[7]直接与pattern[3]对齐。
另一方面,如果target[n]与pattern[k]对齐,但是pattern[k+1...j]在模式串中不存在包含后缀,我们该如何决定模式串向后的滑动距离呢。此时target[n+1...n+j-k] = pattern[k+1...j],因为pattern[k+1...j]不存在包含后缀,所以对于任何m(0<=m
此时可以考虑pattern[1]与target[n+1]对齐。pattern[1]与target[n+1]对齐后,pattern[1...j-k]是模式的前缀j-k,target[n+1...n+j-k]相当于pattern[k+1...j],因为pattern[k+1...j]不存在包含子串,所以此次匹配也会失败。继续移动pattern[1],pattern[1]与target[n+2]对齐,此时target[n+2...n+j-k]相当于pattern[1...j-k-1],与pattern[k+2...j]比较,此时两者是否相等依赖于我们之前计算pre表的结果,能够使这个匹配成立的是使pattern[1...m]=pattern[j-m+1...j]的m的最大值,将pattern[1]与target[n+j-k-m+1]对齐,如果这样的m不存在,则pattern[1]可以直接与target[n+j-k+1]对齐,再执行匹配。如下例,当在target[4]处发生匹配失败,根据之前的介绍,pattern[1]与2,3,4,5,6对齐也都会失败,这里j=9,k=4,m=3,n=4。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
target | A | B | C | D | X | X | A | B | C | . | . | . | . | . | . |
A | B | C | X | X | X | A | B | C | |||||||
A | B | C | D | X | X | A | B | C | |||||||
A | B | C | D | X | X | A | B | C |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
B | C | D | B | C | D | A | B | C | D | A | B | C | D | |
pre[i] | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 3 | 14 | 14 | |
goods[i] | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 11 | 13 | 12 | 1 |
现在BM算法的两个基本工具坏字符,好后缀都已具备,我们如何在目标串target[1...n]中飞快的找到我们想要的模式pattern[1..j]呢。
首先,我们将pattern[1]与target[1]对齐,然后从target[j]向前依次执行匹配操作。如果在pattern[i]位置发现匹配失败,则在好前缀表里用i查找滑动距离goods[i],在坏字符表中用target[i]做索引,查找滑动距离badc[target[i]],假设前者返回的值为p,后者返回的值为q,这时我们取其中的较大者(假设为p),然后将pattern[j]与target[i+p]对齐,然后依次向前匹配,直到发现匹配,或者遍历整个target串没有找到目标模式为止。下面是BM算法的实现代码,该算法与之前KMP算法一样,都进行了扩展,可以找到目标串中的所有匹配模式,相比之下,BM扩展为找到目标序列中的所有匹配模式串要比KMP简单,不需要引入任何新的东西,只需要在发现匹配模式之后,仍然按照goods[0]移动目标串游标即可。
后记:
二.
在用于查找子字符串的算法当中,BM(Boyer-Moore)算法是目前相当有效又容易理解的一种,一般情况下,比KMP算法快3-5倍。
BM算法在移动模式串的时候是从左到右,而进行比较的时候是从右到左的。
BM算法实际上包含两个并行的算法,坏字符算法和好后缀算法。这两种算法的目的就是让模式串每次向右移动尽可能大的距离(j+=x,x尽可能的大)。
几个定义:
例主串和模式串如下:
主串 : mahtavaatalomaisema omalomailuun
模式串: maisemaomaloma
好后缀:模式串中的aloma为“好后缀”。
坏字符:主串中的“t”为坏字符。
如果程序匹配了一个好后缀, 并且在模式中还有另外一个相同的后缀, 那
把下一个后缀移动到当前后缀位置。好后缀算法有两种情况:
Case1:模式串中有子串和好后缀安全匹配,则将最靠右的那个子串移动到好后缀的位置。继续进行匹配。
Case2:如果不存在和好后缀完全匹配的子串,则在好后缀中找到具有如下特征的最长子串,使得P[m-s…m]=P[0…s]。说不清楚的看图。
当出现一个坏字符时, BM算法向右移动模式串, 让模式串中最靠右的对应字符与坏字符相对,然后继续匹配。坏字符算法也有两种情况。
Case1:模式串中有对应的坏字符时,见图。
Case2:模式串中不存在坏字符。见图。
移动规则
BM算法的移动规则是:
将概述中的++j,换成j+=MAX(shift(好后缀),shift(坏字符)),即
BM算法是每次向右移动模式串的距离是,按照好后缀算法和坏字符算法计算得到的最大值。
shift(好后缀)和shift(坏字符)通过模式串的预处理数组的简单计算得到。好后缀算法的预处理数组是bmGs[],坏字符算法的预处理数组是BmBc[]。
BM算法子串比较失配时,按坏字符算法计算模式串需要向右移动的距离,要借助BmBc数组。
注意BmBc数组的下标是字符,而不是数字。
BmBc数组的定义,分两种情况。
1、 字符在模式串中有出现。如下图,BmBc[‘k’]表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。
2、 字符在模式串中没有出现:,如模式串中没有字符p,则BmBc[‘p’] = strlen(模式串)。
BM算法子串比较失配时,按好后缀算法计算模式串需要向右移动的距离,要借助BmGs数组。
BmGs数组的下标是数字,表示字符在模式串中位置。
BmGs数组的定义,分三种情况。
1、 对应好后缀算法case1:如下图:i是好后缀之前的那个位置。
2、 对应好后缀算法case2:如下图所示:
3、 当都不匹配时,BmGs[i] = strlen(模式串)
在计算BmGc数组时,为提高效率,先计算辅助数组Suff。
Suff数组的定义:suff[i] = 以i为边界, 与模式串后缀匹配的最大长度,即P[i-s...i]=P[m-s…m]如下图:
举例如下:
分析
用Suff[]计算BmGs的方法。
1) BmGs[0…m-1] = m;(第三种情况)
2) 计算第二种情况下的BmGs[]值:
for(i=0;i
if(-1==i || Suff[i] == i+1)
for(;j < m-1-i;++j)
if(suff[j] == m)
BmGs[j] = m-1-i;
3) 计算第三种情况下BmGs[]值,可以覆盖前两种情况下的BmGs[]值:
for(i=0;i
BmGs[m-1-suff[i]] = m-1-i;
如下图所示:
Suff[]数组的计算方法。
常规的方法:如下,很裸很暴力。
Suff[m-1]=m;
for(i=m-2;i>=0;--i){
q=i;
while(q>=0&&P[q]==P[m-1-i+q])
--q;
Suff[i]=i-q;
}
有聪明人想出一种方法,对常规方法进行改进。基本的扫描都是从右向左。改进的地方就是利用了已经计算得到的suff[]值,计算现在正在计算的suff[]值。
如下图所示:
i是当前正准备计算的suff[]值得那个位置。
f是上一个成功进行匹配的起始位置(不是每个位置都能进行成功匹配的, 实际上能够进行成功匹配的位置并不多)。
q是上一次进行成功匹配的失配位置。
如果i在q和f之间,那么一定有P[i]=P[m-1-f+i];并且如果suff[m-1-f+i]=i-q, suff[i]和suff[m-1-f+i]就没有直接关系了。
1 int BMMatch(byte* pSrc, int nSrcSize, byte* pSubSrc, int nSubSrcSize)
2 {
3
4 //1.坏字符数组
5 int bcSkip[256];
6 for( int i = 0; i < 256; i++)
7 {
8 bcSkip[i] = nSubSrcSize;
9 }
10 for (int i = 0; i < nSubSrcSize - 1; i++)
11 {
12 bcSkip[pSubSrc[i]] = nSubSrcSize - i - 1;
13 }
14
15 //2.好后缀数组
16 int* suffix = new int [nSubSrcSize];
17 suffix[nSubSrcSize - 1] = nSubSrcSize;
18 for (int i = nSubSrcSize - 2; i >= 0; i--)
19 {
20
21 int k = i;
22 while( k >= 0 && pSubSrc[k] == pSubSrc[nSubSrcSize-1-i+k] )
23 {
24 k--;
25 }
26 suffix[i] = i - k;
27 }
28
29 int* gsSkip = new int [nSubSrcSize];
30 for (int i = 0; i < nSubSrcSize; i++)
31 {
32 gsSkip[i] = nSubSrcSize;
33 }
34 for (int i = nSubSrcSize - 1; i >= 0; i--)
35 {
36 if (suffix[i] == i + 1)
37 {
38 for (int j = 0; j < nSubSrcSize - 1 - i; ++j)
39 {
40 if (gsSkip[j] == nSubSrcSize)
41 gsSkip[j] = nSubSrcSize - 1 - i;
42 }
43 }
44 }
45 for (int i = 0; i <= nSubSrcSize - 2; ++i)
46 {
47 gsSkip[nSubSrcSize - 1 - suffix[i]] = nSubSrcSize - 1 - i;
48 }
49
50 int nPos = 0;
51 while (nPos <= nSrcSize - nSubSrcSize)
52 {
53 int j = nSubSrcSize - 1;
54 while(j >= 0 && pSubSrc[j] == pSrc[j + nPos])
55 {
56 j--;
57 }
58 if (j < 0)
59 break;
60 else
61 {
62 nPos += max(gsSkip[j], bcSkip[pSrc[j + nPos]]-(nSubSrcSize - 1 - j) );
63 }
64 }
65 delete[] gsSkip;
66 return (nPos > nSrcSize - nSubSrcSize)? -1 : nPos;
67 }
1 int BMMatchEx(byte* pSrc, int nSrcSize, byte* pSubSrc, int nSubSrcSize)
2 {
3
4 //1.坏字符数组
5 int bcSkip[256];
6 for( int i = 0; i < 256; i++)
7 {
8 bcSkip[i] = nSubSrcSize;
9 }
10 for (int i = 0; i < nSubSrcSize - 1; i++)
11 {
12 bcSkip[pSubSrc[i]] = nSubSrcSize - i - 1;
13 }
14
15 //2.好后缀数组
16 int* suffix = new int [nSubSrcSize];
17 suffix[nSubSrcSize - 1] = nSubSrcSize;
18 int g = nSubSrcSize - 1;
19 int f = 0;
20 for (int i = nSubSrcSize - 2; i >= 0; i--)
21 {
22 if(i > g && suffix[i + nSubSrcSize - 1 - f] < i - g)
23 {
24 suffix[i] = suffix[i + nSubSrcSize - 1 - f];
25 }
26 else
27 {
28 if (i < g)
29 {
30 g = i;
31 }
32 f = i;
33 while( g >= 0 && pSubSrc[g] == pSubSrc[nSubSrcSize-1-f+g] )
34 {
35 g--;
36 }
37 suffix[i] = f - g;
38 }
39 }
40
41 int* gsSkip = new int [nSubSrcSize];
42 for (int i = 0; i < nSubSrcSize; i++)
43 {
44 gsSkip[i] = nSubSrcSize;
45 }
46 for (int i = nSubSrcSize - 1; i >= 0; i--)
47 {
48 if (suffix[i] == i + 1)
49 {
50 for (int j = 0; j < nSubSrcSize - 1 - i; ++j)
51 {
52 if (gsSkip[j] == nSubSrcSize)
53 gsSkip[j] = nSubSrcSize - 1 - i;
54 }
55 }
56 }
57 for (int i = 0; i <= nSubSrcSize - 2; ++i)
58 {
59 gsSkip[nSubSrcSize - 1 - suffix[i]] = nSubSrcSize - 1 - i;
60 }
61
62 int nPos = 0;
63 while (nPos <= nSrcSize - nSubSrcSize)
64 {
65 int j = nSubSrcSize - 1;
66 while(j >= 0 && pSubSrc[j] == pSrc[j + nPos])
67 {
68 j--;
69 }
70 if (j < 0)
71 break;
72 else
73 {
74 nPos += max(gsSkip[j], bcSkip[pSrc[j + nPos]]-(nSubSrcSize - 1 - j) );
75 }
76 }
77 delete[] gsSkip;
78 return (nPos > nSrcSize - nSubSrcSize)? -1 : nPos;
79 }
1 |
j = 0; |
2 |
while (j <= strlen(T) - strlen(P)) { |
3 |
for (i = strlen(P) - 1; i >= 0 && P[i] ==T[i j]; --i) |
4 |
if (i < 0) |
5 |
match; |
6 |
else |
7 |
j; |
8 |
} |
为了实现更快移动模式串,BM算法定义了两个规则,好后缀规则和坏字符规则,如下图可以清晰的看出他们的含义。利用好后缀和坏字符可以大大加快模式串的移动距离,不是简单的 j,而是j =max (shift(好后缀), shift(坏字符))
先来看如何根据坏字符来移动模式串,shift(坏字符)分为两种情况:
为了用代码来描述上述的两种情况,设计一个数组bmBc['k'],表示坏字符‘k’在模式串中出现的位置距离模式串末尾的最大长度,那么当遇到坏字符的时候,模式串可以移动距离为: shift(坏字符) = bmBc[T[i]]-(m-1-i)。如下图:
数组bmBc的创建非常简单,直接贴出代码如下:
1 |
void preBmBc(char *x, int m, int bmBc[]) { |
2 |
int i; |
3 |
for (i = 0; i < ASIZE; i) |
4 |
bmBc[i] = m; |
5 |
for (i = 0; i < m - 1; i) |
6 |
bmBc[x[i]] = m - i - 1; |
7 |
} |
再来看如何根据好后缀规则移动模式串,shift(好后缀)分为三种情况:
为了实现好后缀规则,需要定义一个数组suffix[],其中suffix[i] = s 表示以i为边界,与模式串后缀匹配的最大长度,如下图所示,用公式可以描述:满足P[i-s, i] == P[m-s, m]的最大长度s。
构建suffix数组的代码如下:
1 |
suffix[m-1]=m; |
2 |
for (i=m-2;i>=0;--i){ |
3 |
q=i; |
4 |
while(q>=0&&P[q]==P[m-1-i q]) |
5 |
--q; |
6 |
suffix[i]=i-q; |
7 |
} |
有了suffix数组,就可以定义bmGs[]数组,bmGs[i] 表示遇到好后缀时,模式串应该移动的距离,其中i表示好后缀前面一个字符的位置(也就是坏字符的位置),构建bmGs数组分为三种情况,分别对应上述的移动模式串的三种情况
构建bmGs数组的代码如下:
01 |
void preBmGs(char *x, int m, int bmGs[]) { |
02 |
int i, j, suff[XSIZE]; |
03 |
suffixes(x, m, suff); |
04 |
for (i = 0; i < m; i) |
05 |
bmGs[i] = m; |
06 |
j = 0; |
07 |
for (i = m - 1; i >= 0; --i) |
08 |
if (suff[i] == i 1) |
09 |
for (; j < m - 1 - i; j) |
10 |
if (bmGs[j] == m) |
11 |
bmGs[j] = m - 1 - i; |
12 |
for (i = 0; i <= m - 2; i) |
13 |
bmGs[m - 1 - suff[i]] = m - 1 - i; |
14 |
} |
再来重写一遍BM算法:
1 |
j = 0; |
2 |
while (j <= strlen(T) - strlen(P)) { |
3 |
for (i = strlen(P) - 1; i >= 0 && P[i] ==T[i j]; --i) |
4 |
if (i < 0) |
5 |
match; |
6 |
else |
7 |
j = max(bmGs[i], bmBc[T[i]]-(m-1-i)); |
8 |
} |
考虑模式串匹配不上母串的最坏情况,后缀蛮力匹配算法的时间复杂度最差是O(n×m),最好是O(n),其中n为母串的长度,m为模式串的长度。
#include
#include
#define XSIZE 8
#define ASIZE 256
void preBmBc(char *x, int bmBc[])
{
int m;
int i;
m = strlen(x);
for (i = 0; i < ASIZE; i )
{
bmBc[i]=m;
}
for (i = 0; i < m - 1; i )
{
bmBc[x[i]]=m-1-i;
}
}
void suffixes(char *x, int *suff)
{
int i;
int f;
int g;
int m;
m = strlen(x);
f = 0;
suff[m-1] = m;
g = m - 1;
for (i = m - 2; i >= 0; --i)
{
if (i > g && suff[(m-1)-(f-i)] < i - g)
{
suff[i] = suff[(m-1)-(f-i)];
}
else
{
if (i < g)
g=i;
f=i;
while (g >= 0 && x[g] == x[g m - 1 -f])
{
g--;
}
suff[i] = f - g;
}
}
}
void preBmGs(char *x, int bmGs[])
{
int i;
int j;
int suff[XSIZE];
int m;
m=strlen(x);
suffixes(x, suff);
for (i = 0; i < m; i)
{
bmGs[i] = m;
}
j = 0;
for (i = m-1;i >= 0; --i)
{
if (suff[i] == i 1)
{
for (; j < m - 1 - i; j)
{
if (bmGs[j] == m)
{
bmGs[j] = m - 1 - i;
}
}
}
}
for (i = 0; i <= m-2; i)
{
bmGs[m - 1 -suff[i]]= m - 1 - i;
}
}
int BM(char *x,char *y)
{
int m;//模式串的长度
int n;//目标串的长度
int i;//模式串中匹配的位置
int j;//目标串中匹配的位置
int bmGs[XSIZE];//好后缀表
int bmBc[ASIZE];//坏字符表
preBmBc(y, bmBc);
preBmGs(y, bmGs);
m = strlen(y);
n = strlen(x);
j=0;
while (j < n-m )
{
for (i = m - 1; i >= 0 && y[i] == x[i j]; --i);
if (i < 0)
{
return j;
}
else
{
j = (bmGs[i]) > (bmBc[x[i j]] -m 1 i) ? (bmGs[i]) : (bmBc[x[i j]] -m 1 i);
}
}
if (j>=n-m)
{
return -1;
}
}
int main(int argc,char **argv)
{
int ret;
ret=BM(argv[1],argv[2]);
printf("ret is %d\n",ret);
return 0;
}