BM算法

BM算法是一种字符串匹配的算法。
与KMP相比,BM算法不扫描全部输入字符,平均匹配时间c·n, 常量 c <1 (随机或真实文本), 但最坏情况是O(n·m).
可以将BM算法的最坏情况改进到O(n):通过记录文本后缀中最长的模式后缀。
要使用BM算法,需要知道两个信息:
1.用于坏字符规则的bc数组
2.用于好后缀规则的gs数组

坏字符规则

坏字符规则分为两种情况:
1.失配位置指向的文本串中对应的字符 ,不存在于模式串中。
坏字符
如上图所示,在这种情况下,直接将整个模式串移动到失配位置 之后。

2.失配位置指向的文本串中对应的字符 ,存在于模式串中,且在失配位置 的左边。
坏字符
如上图所示,在这种情况下,将模式串中的文本串中对应的字符 放在失配位置 上。
需要注意两个问题:
1.这个“模式串中的文本串中对应的字符 ”,是整个模式串从右往左数的第一个符合的字符。否则会造成过度左移。
2.模式串中最后一个字符,不能和任何的失配位置 匹配。这是因为“失配”的前提是有匹配,而右边第一个字符必然被匹配;否则在右边第一个字符失配,那说明所需要的字符不是这个右边的第一个字符。故最后一个字符对应的位置是从右边数第二个符合数的位置。
如字符串“GCAGAGAG”的坏字符表为(从0开始计数,从右往左数):

char A C G T
bc[char] 1 6 2 8

坏字符表不是一直有效的。如果坏字符表中记载的位置,在失配位置右边,那么可能会造成负位移或原地不动。
一个解决方法是,记载每次失配位置左边的第一个符合的字符:但这很麻烦。
这并不是说位移就是上表的值。位移= bc[char]-失配位置Z。(从右往左数,0开始)

好后缀规则

好后缀规则分为3种情况:
坏字符
rule3:

如图14.1,目前匹配好的后缀u,在模式串中存在。如果有多个,则取最靠右的且c!=a的那个,并将其对齐。

rule2:

如图14.2,目前匹配的好后缀u,在模式串中其他位置不存在。但它的后缀,和模式串的前缀相同。如果有多个满足的后缀,则取最长的那个后缀,并将其对齐。

rule1:

如图14.?,目前匹配的好后缀u,在模式串中其他位置不存在。且它的每一个后缀,和模式串的前缀都不相同。这种情况下,直接将整个模式串移动到当前的最右端之后。

好后缀规则和坏字符规则是可以同时使用的,我们每次取俩者中最大的那个。
如坏字符规则一样,好后缀也有自己的表,叫做gs数组。要想得到gs数组,首先要利用suff数组。

suff数组:存储从字符s[i]向左开始计数的,和模式串最右边字符开始的匹配的字符个数。

如字符串“GCAGAGAG”的suff为(从0开始计数):

G C A G A G A G
1 0 0 2 0 4 0 8

从右往左看:
从G开始,GAGAGACG与GAGAAGACG匹配,个数是8,故填8。
从A开始,没有字符匹配(因为右边第一个字符是G),故填0。
从G开始,GAGA(下一个是C)与GAGA(下一个是G)匹配,个数是4,故填4。
从A开始,没有字符匹配(因为右边第一个字符是G),故填0。
从G开始,GA(下一个是C)与GA(下一个是G)匹配,个数是2,故填2。
从A开始,没有字符匹配(因为右边第一个字符是G),故填0。
从C开始,没有字符匹配(因为右边第一个字符是G),故填0。
从G开始,G(下一个是末尾)与G(下一个是A)匹配,个数是1,故填1。

接下来,我们依次处理rule1,rule2,rule3,来获得gs数组。
为什么是这个顺序:因为rule1位移>rule2位移>rule3位移,大的值被小的值取代,才不会造成过度右移
具体的理解,可以见高效字符串匹配算法
施加rule1:
所有的项均有最大位移8(字符串长):

G C A G A G A G
8 8 8 8 8 8 8 8

施加rule2:
step1:从右往左扫描模式串,找到第一个下标(下标从左往右数,从0开始计。略过最右边的数)+1=suff[i]的位置。对上面的例子来说,这个位置是“i=0”,对应的是最左边的字符G。
step2:从左往右扫描字符串,将扫描过的位置对应的gs数组改为“当前值-suff[i]”,直到剩下suff[i]个字符。
step3:step继续向左,step2继续向右,直到扫描完成。

G C A G A G A G
7 7 7 7 7 7 7 8

施加rule3:
从左往右扫描字符串(略过最右边那个),将gs[suff[i]]改为i(下标从右往左数,以0开始):

变化1:

G C A G A G A G
7 7 7 7 7 7 7 8

变化2:

G C A G A G A G
7 7 7 7 7 4 7 5

变化3:

G C A G A G A G
7 7 7 2 7 4 7 3

变化4:

G C A G A G A G
i=0 i=1 i=2 i=3 i=4 i=5 i=6 i=7
7 7 7 2 7 4 7 1

一个实例

实例
向右位移=max(bc[A]-Z,gs[7])=max(1-0,1)=1;
实例
向右位移=max(bc[C]-Z,gs[5])=max(6-2,4)=4;
实例
向右位移=max(0,gs[-1---->0] )=max(0,7)=7;
实例
向右位移=max(bc[C]-Z,gs[5])=max(6-2,4)=4;
实例
向右位移=max(bc[C]-Z,gs[6])=max(6-1,7)=7;
附:对于字符串aaaaaa,其gs数组为{6,6,6,6,6,6}—>{1,2,3,4,5,6}—–>{1,2,3,4,5,6}。

与kmp比较

1.KMP算法的实际性能不好,一般实际中不用
2.BM速度快,但最快的BM类算法不是完整BM算法而是简化的版本(复杂度和效率的折中版本)
若模式串长为m,文本串长为n:
BM算法最好情况下比较m次,最坏情况下比较(n-m+1)*m次
MP算法最好情况下比较m次,最坏情况下比较(n-m+1)*m次
平均比较次数:?

参考内容:
1.BM-c++