字符串匹配的Boyer-Moore算法

来源:阮一峰

上一篇文章,我介绍了KMP算法

但是,它并不是效率最高的算法,实际采用并不多。各种文本编辑器的”查找”功能(Ctrl+F),大多采用Boyer-Moore算法

字符串匹配的Boyer-Moore算法

Boyer-Moore算法不仅效率高,而且构思巧妙,容易理解。1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了这种算法。

下面,我根据Moore教授自己的例子来解释这种算法。

1.

字符串匹配的Boyer-Moore算法

假定字符串为”HERE IS A SIMPLE EXAMPLE”,搜索词为”EXAMPLE”。

2.

字符串匹配的Boyer-Moore算法

首先,”字符串”与”搜索词”头部对齐,从尾部开始比较。

这是一个很聪明的想法,因为如果尾部字符不匹配,那么只要一次比较,就可以知道前7个字符肯定不是要找的结果。

我们看到,”S”与”E”不匹配。这时,“S”就被称为”坏字符”(bad character),即不匹配的字符。我们还发现,”S”不包含在搜索词”EXAMPLE”之中,这意味着可以把搜索词直接移到”S”的后一位。

3.

字符串匹配的Boyer-Moore算法

依然从尾部开始比较,发现”P”与”E”不匹配,所以”P”是”坏字符”。但是,”P”包含在搜索词”EXAMPLE”之中。所以,将搜索词后移两位,两个”P”对齐。

4.

字符串匹配的Boyer-Moore算法

我们由此总结出“坏字符规则”

  后移位数 = 坏字符的位置 – 搜索词中的上一次出现位置

如果”坏字符”不包含在搜索词之中,则上一次出现位置为 -1。

以”P”为例,它作为”坏字符”,出现在搜索词的第6位(从0开始编号),在搜索词中的上一次出现位置为4,所以后移 6 – 4 = 2位。再以前面第二步的”S”为例,它出现在第6位,上一次出现位置是 -1(即未出现),则整个搜索词后移 6 – (-1) = 7位。

5.

字符串匹配的Boyer-Moore算法

依然从尾部开始比较,”E”与”E”匹配。

6.

字符串匹配的Boyer-Moore算法

比较前面一位,”LE”与”LE”匹配。

7.

字符串匹配的Boyer-Moore算法

比较前面一位,”PLE”与”PLE”匹配。

8.

字符串匹配的Boyer-Moore算法

比较前面一位,”MPLE”与”MPLE”匹配。我们把这种情况称为”好后缀”(good suffix),即所有尾部匹配的字符串。注意,”MPLE”、”PLE”、”LE”、”E”都是好后缀。

9.

比较前一位,发现”I”与”A”不匹配。所以,”I”是”坏字符”。

10.

根据”坏字符规则”,此时搜索词应该后移 2 – (-1)= 3 位。问题是,此时有没有更好的移法?

11.

我们知道,此时存在”好后缀”。所以,可以采用“好后缀规则”

  后移位数 = 好后缀的位置 – 搜索词中的上一次出现位置

计算时,位置的取值以”好后缀”的最后一个字符为准。如果”好后缀”在搜索词中没有重复出现,则它的上一次出现位置为 -1。

所有的”好后缀”(MPLE、PLE、LE、E)之中,只有”E”在”EXAMPLE”之中出现两次,所以后移 6 – 0 = 6位。

12.

可以看到,”坏字符规则”只能移3位,”好后缀规则”可以移6位。所以,Boyer-Moore算法的基本思想是,每次后移这两个规则之中的较大值。

更巧妙的是,这两个规则的移动位数,只与搜索词有关,与原字符串无关。因此,可以预先计算生成《坏字符规则表》和《好后缀规则表》。使用时,只要查表比较一下就可以了。

13.

继续从尾部开始比较,”P”与”E”不匹配,因此”P”是”坏字符”。根据”坏字符规则”,后移 6 – 4 = 2位。

14.

从尾部开始逐位比较,发现全部匹配,于是搜索结束。如果还要继续查找(即找出全部匹配),则根据”好后缀规则”,后移 6 – 0 = 6位,即头部的”E”移到尾部的”E”的位置。

3 收藏 16 评论

相关文章

可能感兴趣的话题



直接登录
最新评论
  • null   2013/05/04

    说实话,我对“搜索词中的上一次出现位置”这样的表述不是很理解,不过经过反复推敲,我认为是“坏字符在搜索词中的位置”,这样就可以统一234步了。

  • loyuk   2013/05/05

    思想理解了,只是:“所有的”好后缀”(MPLE、PLE、LE、E)之中,只有”E”在”EXAMPLE”之中出现两次,所以后移 6 – 0 = 6位。”这句话没有明白,目测没看出有什么因果关系

    • sakyo   2013/05/05

      就是把simple中的E当做坏字符,然后找在example中出现了2次,就和第一个E对齐。这么就理解了

    • binzai1991   2013/05/05

      应该是说明搜索词内"MPLE"连在一起的字符串只有一个。所以无需再比较,直接跳过整个搜索词,从下一个点开始比较。

    • Gemini   2013/05/05

      不用管这个,就是末尾那个e在example中位子是6(下标从0开始),在example中e上一次出现的位子是0,所以6-0=6.我是这么理解的,不知道对不对

    • KevinCao   2013/05/06

      其实,所有的所谓的“好后缀”都是以“EXAMPLE”中的“E”结尾的,因此只要找到“E”在“EXAMPLE”中出现的倒数第二个位置就可以了。

      • freeboy1015   2013/05/07

        "BmBc数组的定义,分两种情况。

        1、 字符在模式串中有出现。如下图,BmBc[‘k’]表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。

        我对这个也有疑惑:情况1中为什么BmBc[‘k’]是表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。如果有多个k出现呢?

        假设:

        T:abcdbccg

        P: abcdbecg

        现在c和e不匹配,按照你的上面的方法计算的BmBc[‘c’]等于1,显然不对啊!

  • 海盗Andy   2013/05/05

    学生求伪代码呀!!有注释的~多谢!

  • houhl   2013/05/06

    加了good suffix变难了啊

  • fanggx   2013/05/06

    个人觉得,图可能是对的,但是你的讲解让人费解。
    个人觉得应该这么理解:目标字符串EXAMPLE,源字符串HERE IS A SIMPLE EXAMPLE
    源字符串中需要比较的字符和目标字符串中最后一个字符相比
    1.不相同,直接后退目标字符串长度
    2.相同,有两个情况 A、比较上一位
    B、目标字符串和源字符串有不同字符时,检查目标字符串中最后一个和其从后面到前面最近的哪个字符相同,移动位置为这两个相同字符之间的间隔,如果没有那就是整个目标字符串的长度。

  • freeboy1015   2013/05/07

    "BmBc数组的定义,分两种情况。

    1、 字符在模式串中有出现。如下图,BmBc[‘k’]表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。

    我对这个也有疑惑:情况1中为什么BmBc[‘k’]是表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。如果有多个k出现呢?

    假设:

    T:abcdbccg

    P: abcdbecg

    现在c和e不匹配,按照你的上面的方法计算的BmBc[‘c’]等于1,显然不对啊!

  • macazy   2013/05/09

    如果可以给出实现就更好了

  • heart162   2013/05/25

    想问下预处理怎么做……因为考虑的是英文搜索,最多255个字符,如果是中文搜索怎么办?预处理岂不是会死人?求指点

  • terry   2013/05/25

    对于这一句“Boyer-Moore算法的基本思想是,每次后移这两个规则之中的较大值。”
    我觉得应该是 每次后移两个规则中的较小值吧,不应该是较大值吧。。。

  • muzhi   2013/05/30

    为什么最后的图片是PS的,我表示鸭梨很大。

  • Nap   2013/08/10

    为什么后面的图标全都乱入了..

跳到底部
返回顶部