数据结构(邓俊辉)学习笔记】串 10——BM_BC算法:坏字符

文章目录

1.坏字符

实际上,刚才的实例中我们所展示的那样一个计算过程,就是所谓 BM 算法所采用的策略之一,而这一策略,将我们刚才所说的教训称作坏字符。

在这里,不妨改为基于蛮力算法的第二个版本来进行改造。也就是说模式算中当前参与比对的如果是字符 P[j],那么文本串中对应的就是 T[i + j], 这幅图画出的就是这样一个一般性的场景。

请注意,当前这趟扫描如果的确已经抵达 P[j] 或 T[i + j],则说明在这对字符的右侧,有一段足够长的匹配部分。而相对于模式串而言,这个匹配部分是一个后缀。既然这两个字符失配,不妨就分别将它们记作 X 和 Y。

延续我们在刚才的分析中所采用的逻辑,如果我们将这次失配当做一次教训,那么其原因就可以解释为:在 Y 这个位置,我本来期望是一个 X,然而这个字符Y的出现,却使得我们在此局部获得一次完整匹配的希望成了泡影,因此 BM 算法也将这个字符 Y 称作坏字符。

正是因为这个坏字符的出现,当前这个对齐位置,也就自然地可以被排除掉了。那么,更重要的是,这次教训能够为我们带来什么启示呢?具体的能为我们后续的比对提供什么有益的借鉴呢?

将刚才我们所举的实例推越广之,如果我们还希望能够在这个字符 X 附近,实现一次完整的匹配,那么作为一项必要条件,与这个字符 X 对准的也应该至少是一个 X。是的,由刚才所举的实例推而广之,我们也同样可以得出此时的处置方法。

具体来说,就是试图从模式上中去找出这样的一个字符 X,并且通过相应的移动,使得这个 X 能与文本串中的那个 X 彼此对齐。而一旦完成了这样的对齐,我们又可以继而从最右端开始,启动下一轮的扫描比对。

那么对应于每一个这样的场景,相应的位移量又应该是多少呢?

稍加观察我们不难发现,这个位移量仅取决于刚才失配的位置 j 以及对应的字符 X 在模式传中的位置。而与文本串以及当前的对齐位置没有任何关系。这就意味着我们可以与 KMP 算法一样通过预处理事先计算出每种情况下对应的位移量。

也就是说,我们可以将每一个这样的字符所对应的位移量制成一个表,也就是所谓的 bc 表。通过再进一步观察,我们不能发现,这个表只需记录每一个字符 X 在模式串中所对应的秩,在图中也就对应于这段距离(bc['X'])。

我们知道,这段前缀的长度恰好就应该是 j,而对应的位移量也自然就应该是这两段前缀的长度之差。

2. 特殊情况

当然,实际情况要更为复杂一些。不妨来看看还有哪些特殊情况需要考虑并且处置。

  1. 首先需要考虑的一种情况就是,在模式串中有可能同时存在多个 X,那在这种情况下,我们究竟应该用哪个 X 来替换哪个坏字符 Y 呢?与 KMP 算法一样,当同时存在多个候选时,我们并不倾向于向数学上那样,逐一地去尝试它们,而应该在安全的前提下,选择其一,从而使得我们不必回溯。

也就是说,如果有多个候选,那么我们倾向于选择其中对应的位移量尽可能小的那个。按照刚才的分析,任何一个字符所对应的位移量都与这个字符在模式串中的秩呈反比。因此,为了使得位移量尽可能的小,所对应字符的秩就应该尽可能地大。换而言之,当有多个候选时,我们应该首先选用其中的最靠后者,或者等价的,在相对于这个字符 X 的那个后缀中,必须不包含任何 X。

  1. 那么与此相反的另一种极端情况就是,在模式串中,可能没有任何这样的 X 可供选择。

    回忆一下,在我们此前所举的那个实例中,的确就有这种情况。比如其中文本串中的那个"道"字,在模式串中根本就没有出现过。你应该还记得我们当时的处置方法,没错,我们只需将模式串完整的移过这个对齐的位置。那么在相应的算法描述以及代码实现的过程中,我们又应该如何统一地来处置这种情况呢?没错,哨兵。

    与 KMP 算法一样,我们可以为每一个模式串在最左侧增设一个假想的通配哨兵。于是所谓的将整个模式串移过这个位置,也就等效于用这个通配的哨兵与刚才失配的字符对齐。既然是通配字符,那么此前适配的这个字符在此后就可以被忽略掉。

实际上,除了以上所介绍的两种,还有一种更为特殊的情况需要处理,你能看出来吗?是的,在这种情况下,模式串中的确至少存在一个候选的字符 X。然而,其中最靠右的那个,位置又过于靠右。也就说,它所对应的那个 bc 表项,或者说他的秩太大了,高过了 j,以至于通过二者之差所计算出来的位移量,既然是一个负数。显然,这样的回溯是没有道理的,也是不必要的。

因为作为这个算法的一条不变性,相对于任何时候的当前对齐位置,此前的所有对齐位置,都已经被淘汰掉了,因此根本没有必要再回过头去重新考虑。既然以前的所有对齐位置都已被淘汰掉,而当前的这个对齐位置也刚刚被否定掉了。所以再直接而简洁不过的处置方法就是,将整个模式串向后移动一个字符,并进而考察下一个对齐位置。

至此,我们已经兼顾到了可能发生的所有情况,包括特殊情况。我知道 KMP 算法的整个策略,完全是融入并体现于其中的 next 表。而这里的 BM 算法也类似,其策略也将最终融入并体现于 bc 表,以及稍后要介绍的 gs 表。

那么首先一个问题就是 bc 表又当如何构造出来呢?相应的我们又需要花费多长时间呢?

相关推荐
WarPigs15 分钟前
blender场景导入Unity的流程(个人总结)
笔记
姜威鱼1 小时前
蓝桥杯python编程每日刷题 day 21
数据结构·算法·蓝桥杯
Song2 小时前
JVM 学习计划表(2025 版)
jvm·学习
神里流~霜灭2 小时前
蓝桥备赛指南(12)· 省赛(构造or枚举)
c语言·数据结构·c++·算法·枚举·蓝桥·构造
小杨爱学习zb2 小时前
学习总结 网格划分+瞬态求解设置
笔记·学习·算法
双叶8362 小时前
(C语言)单链表(1.0)(单链表教程)(数据结构,指针)
c语言·开发语言·数据结构·算法·游戏
互联网上的猪2 小时前
Excel时间类型函数(包括today、date、eomonth、year、month、day、weekday、weeknum、datedif)
笔记·学习·excel
weixin_535455793 小时前
WPF设计学习记录滴滴滴2
学习·wpf
学习编程的gas3 小时前
数据结构——队列的实现
数据结构
阿超爱嵌入式3 小时前
STM32学习笔记之RCC模块(实操篇)
笔记·stm32·学习