【算法】字符串匹配2 BM算法 坏字符规则 好后缀规则 python代码实现
BM算法, Boyer-Moore,非常高效,是KMP算法的3~4倍。
高能预警,此算法较难。
核心思想
匹配过程其实就是模式串在主串中不停地往后滑动。
当遇到不匹配的字符时,BF和RK算法做法是往后滑动一位,从模式串第一个字符重新匹配。
上图中,主串中的 c
其实在模式串中并不存在,所以滑动时只要与 c
有重合,肯定无法匹配。
所以可以把模式串多滑动几位,移到c后面再开始匹配。这样效率就提高了。
那么如何寻找这种规律?这就是BM算法的本质了。
算法原理
BM算法包含两部分坏字符规则( bad character rule )和 好后缀规则( good suffix
shift )。
坏字符规则
BM 算法的匹配顺序按照模式串下标从大到小的顺序,倒着匹配的。
倒着匹配时,主串中某个字符在模式串中没法匹配,把这个没有匹配的字符叫作 坏字符(上图中的 c
)。
这时,可以将模式串直接往后滑动三位,将模式串滑动到 c 后面的位置,再从模式串的末尾字符开始比较。
此时,末尾字符是 a
(主串)和 d
(模式串)比较;
两者不匹配,虽然 a
是坏字符,但是字符 a
在模式串中存在;
滑动使两个 a
对齐。
如果出现多个 a
则对齐最先匹配上的(最右边),
对齐后,依然从尾部字符开始匹配。
综上所述找规律,
当发生不匹配时,把坏字符对应的模式串中的字符下标记作 si
;
坏字符在模式串中存在,把这个坏字符在模式串中的下标记作 xi
;
坏字符在模式串中不存在,把 xi 记作 -1
;
模式串往后移动的位数就等于 si-xi
。下标都是字符在模式串的下标。
要说明的是,放坏字符在模式串中多次出现时,计算xi选择最靠后的那个,避免滑动过多从而错过匹配。
坏字符规则,最好情况时间复杂度是。例如主串 aaabaaabaaabaaab
,模式串aaaa
情况,si=3
xi=-1
si-xi=4
,每次直接移动四位,很高效。
但是只是用坏字符规则还不够,比如,主串aaaaaaaaaaaaaaaa
,模式串baaa
,si=0
xi=3
si-xi=-3
,为负值,出现倒退情况。
这时就需要用到好后缀规则。
好后缀规则
从后往前匹配时,已经匹配的部分叫作好后缀,下图中的bc
。
当遇到坏字符时,
已经匹配的好后缀bc
记做{u}
。{u}
在模式串中查找,如果找到相同子项,记做{u*}
,
将{u*}
与主串中的{u}
对齐。如下图。
如果在模式串中没有找到另一个{u}
,
则下一步要查看好后缀的后缀子串,是否存在跟模式串的前缀子串匹配的。结合下图理解这句话。
从好后缀的后缀子串中找到能与之匹配的模式串中的最长的前缀子串,记为{v}
,将模式串滑动使后缀子串与{v}
匹配的位置。
假如后缀子串与前缀子串没有找到匹配,则将模式串移动到主串中 {u} 的后面。
两个规则选择方法
如何选择用好后缀规则还是坏字符规则,来计算模式串往后滑动的位数?
分别计算好后缀和坏字符往后滑动的位数,然后取两个数中最大的作为滑动次数。
代码实现
本文是极客时间王争 数据结构与算法 课程的笔记,推荐此课,喜欢可以购买课程。