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

文章目录

1.坏字符

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

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

请注意,当前这趟扫描如果的确已经抵达 Pj 或 Ti + 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 表又当如何构造出来呢?相应的我们又需要花费多长时间呢?

相关推荐
陈飞EXCEL26 分钟前
北京杰飞 IT 旗下妙码生花少儿Python编程上线两大学习系统
开发语言·python·学习
SLAM必须dunk31 分钟前
Agent入门学习一
学习
瑞码空间1 小时前
JSP与Servlet核心开发与CRUD实战笔记
java·笔记·servlet·jsp
La Pulga2 小时前
【FreeRTOS源码学习笔记】链表的实现
数据结构·笔记·stm32·mcu·链表·嵌入式·freertos
承渊政道2 小时前
Linux网络学习【从NAT与代理服务理解网络转发、探索内网穿透原理、再到认识DNS与ICMP、最后动手部署并测试内网穿透】
linux·网络·学习·内网穿透·dns·nat
栉甜3 小时前
Vue2学习内容(上)
前端·javascript·vue.js·学习
shangjian0073 小时前
K8S学习-基础组件
学习·容器·kubernetes
番茄味城市甜菜3 小时前
数据结构:双向链表实现,链表与顺序表的对比
数据结构·链表
lifallen3 小时前
ub-mode skill:软件工程原则
人工智能·学习·ai·软件工程·ai编程
wuyoutrans4 小时前
以色列专利OA答复翻译,重点要避开哪些坑?
笔记