字符串匹配算法之BM算法理论知识图解

这一节,主要会讲BM算法和KMP算法,在说之前,我们先想象一种场景,文本编辑器里面的查找替换功能,应该都知道吧,比如,我们在word中把一个单词替换成另一个,用的就是这个功能。你有没有想过,这是怎么实现的呢?

当然,用上次专栏说的BF暴力法和RK哈希匹配,也可以实现这个功能,但是BM性能差,RK计算哈希比较复杂,所以实现这项功能又并不是那么简单的。

对于工业级的软件开发来讲,我们希望算法那可以尽可能的高效,并且可以在极端的情况下,性能也不会退化的太严重。

那么,对于查找关键的软件来说,是如何实现的呢?

BM算法核心思想

我们把模式串和主串的匹配过程,看作模式串在主串中不停地往后滑动。当遇到不匹配的字符时,BF算法 和RK算法的做法是,模式串往后滑动一位,然后从模式串的第一个字符开始重新匹配。我举个例子解释一 下,你可以看我画的这幅图。

在这里插入图片描述

在这个例子里,主串中的c,在模式串中是不存在的,所以,模式串向后滑动的时候,只要c与模式串有重 合,肯定无法匹配。所以,我们可以一次性把模式串往后多滑动几位,把模式串移动到c的后面。

在这里插入图片描述

由现象找规律,你可以思考一下,当遇到不匹配的字符时,有什么固定的规律,可以将模式串往后多滑动几 位呢?这样一次性往后滑动好几位,那匹配的效率岂不是就提高了?

我们今天要讲的BM算法,本质上其实就是在寻找这种规律。借助这种规律,在模式串与主串匹配的过程 中,当模式串和主串某个字符不匹配的时候,能够跳过一些肯定不会匹配的情况,将模式串往后多滑动几 位。

BM算法原理分析

BM算法包含两部分,分别是坏字符规则(bad character rule)和好后缀规则 (good suffix shift)。我们下 面依次来看,这两个规则分别都是怎么工作的。

  1. 坏字符规则

前面描述的算法,在匹配的过程中,我们都是按照模式串的下标从小到大的顺序,依次与主串的字符进行匹配的。这种匹配顺序比较符合我们的思维习惯,而BM算法的匹配顺序比较特别,它是按照模式串下标从大到小的顺序倒着匹配的。

在这里插入图片描述

我们从模式串的末尾往前倒着匹配,当我们发现某个字符没法匹配的时候。我们把这个没有匹配的字符叫作 坏字符

在这里插入图片描述

然后我们拿到坏字符c在模式串查找,发现不存在c,那么我们就可以将a后移三位,然后再从末尾字符比较,以此类推。

在这里插入图片描述

这时候我们发现,aca和abd还是对应不上,那么我们就再往后移动3位让bdc对应上我们的模式串abd吗?不是的,因为我们发现aca末尾的a在模式串中是可以对应上的,所以再移动只能让abd对应上abd

在这里插入图片描述
当不匹配的时候吗,我们把坏字符对应的模式串中的字符下标记记做si。如果坏字符在模式串中存在,我们就把这个坏字符在模式串中的下标记做xi。如果不存在,我们就把xi记做-1。那模式串往后移动的位数就等于si-xi(注意,我们这里说的是小表,都是字符在模式串的下标)。

在这里插入图片描述

但是多说一句,如果坏字符在模式串中多次出现,那我们在计算xi的时候,选择最靠后的那个,因为这样不会让模式串滑动过多,导致本来可能匹配的情况被滑动略过。

利用坏字符规则,BM算法在最好的情况时,时间复杂度只有O(n/m)。比如,主串是aaabaaabaaabaaab,模式串是baaa。每次比对都可以后移四位,这种情况就会十分高效,但是也有例外:

如果主串是aaaaaaaaaaaaa,模式串是baaaa。不但不会向后滑动,甚至会倒退,这时候,BM算法还需要用到好后缀原则

  1. 好后缀原则

好后缀规则实际上跟坏字符规则的思路很类似。你看我下面这幅图。当模式串滑动到图中的位置的时候,模 式串和主串有2个字符是匹配的,倒数第3个字符发生了不匹配的情况。

在这里插入图片描述
这个时候该如何滑动模式串呢?当然,我们还可以利用坏字符规则来计算模式串的滑动位数,不过,我们也 可以使用好后缀处理规则。两种规则到底如何选择,我稍后会讲。抛开这个问题,现在我们来看,好后缀规 则是怎么工作的?

我们把已经匹配的bc叫做好后缀,记做{u}。我们拿他在模式串中查找,如果找到了另一个跟{u}相匹配的子串{u*},那我们就将模式串滑动到子串{u*}与主串中{u}对齐的位置。

在这里插入图片描述

如果在模式串中找不到另一个等于{u}的子串,我们就直接将模式串,滑动到主串中{u}的后面,因为之前的 任何一次往后滑动,都没有匹配主串中{u}的情况。

在这里插入图片描述

不过这样做,有可能就会错过正确的匹配答案

在这里插入图片描述
如果好后缀在模式串中不存在可匹配的子串,那在我们一步一步往后滑动模式串的过程中,只要主串中的 {u}与模式串有重合,那肯定就无法完全匹配。但是当模式串滑动到前缀与主串中{u}的后缀有部分重合的时 候,并且重合的部分相等的时候,就有可能会存在完全匹配的情况。

在这里插入图片描述

所以,针对这种情况,我们不仅要看好后缀在模式串中,是否有另一个匹配的子串,我们还要考察好后缀的 后缀子串,是否存在跟模式串的前缀子串匹配的。

所谓某个字符串s的后缀子串,就是最后一个字符跟s对齐的子串,比如abc的后缀子串就包括c, bc。所谓前 缀子串,就是起始字符跟s对齐的子串,比如abc的前缀子串有a,ab。我们从好后缀的后缀子串中,找一个 最长的并且能跟模式串的前缀子串匹配的,假设是{v},然后将模式串滑动到如图所示的位置。

在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值