我们在学习了BF算法和RK算法,那有没更加高效的字符串匹配算法呢。我们今天就来聊一聊BM算法。
BM算法
我们把模式串和主串的匹配过程,可以看做是固定主串,然后模式串不断在往后滑动的过程。当遇到不匹配的字符时,BF算和RK算法的做法是,把模式串向后滑动一位,然后从模式串的第一位开始重新匹配。如下图所示。
由于BF算法和RK算法,在遇到不匹配的字符时,模式串只是向后滑动一位,这样的话时间复杂度比较高,那有没有什么算法可以一下子多滑动几位呢?比如遇到主串A中的字符d,由于d不在模式串中,所以只要d和模式串有重合,那就肯定不能匹配。所以我们可以直接多滑动几位,直接滑到d的后面,然后再继续匹配,这样不就提高了效率了吗?
今天要聊的BM算法,本质上就是寻找这种规律。借助这种规律,在模式串和主串匹配的过程中,当模式串和主串遇到不匹配的字符时,能够跳过一些肯定不匹配的情况,多往后滑动几位。
BM算法的原理
BM算法包含2部分,分别是坏字符规则和好后缀规则。
1.坏字符规则
我们在BF算法和RK算法中,在模式串和主串匹配的过程中,我们都是按模式串的下标从小到大的顺序依次匹配的。而BM算法的匹配顺序则相反,是从大到小匹配的。如下所示。
从模式串的末尾倒着匹配,当发现主串中某个字符匹配不上时,我们就把这个字符称为坏字符。我们拿着坏字符d在模式串中查找,发现d不在模式串中。这个时候,我们可以将模式串直接滑动3位,滑动到字符d的后面,然后再从模式串的末尾开始比较。
这个时候,我们发现主串中的字符串b和模式串的中的c不匹配。这个时候由于坏字符b在模式串中是存在的,模式串中下标为1的位置也是字符b,所以我们可以把模式串向后滑动1位,让主串中的b和模式串中的b相对齐。然后再从模式串的末尾字符开始重新进行匹配。
从上面的例子中,我们可以总结出规律。当发生不匹配的时候,我们把坏字符对应的模式串中的字符下标记做Ai。如果坏字符在模式串中存在,我们把这个坏字符在模式串中的下标记做Bi(如果坏字符在模式串中出现多次,我们把靠后的那个位置记做是Bi,这么做是为了不让模式串向后滑动过多,导致可能匹配的情况错过)。那模式串向后滑动的位数就是Ai-Bi。
不过单纯的使用坏字符规则是不够的。因为根据Ai-Bi计算出来的移动位数有可能是负数。比如主串是aaaaaa,模式串是baaa。所以,BM算法还需要用到“好后缀规则”。
2.好后缀规则
好后缀规则和坏字符规则思路上很相似。如下图所示。
当模式串滑动到图中的位置时,模式串和主串有2个字符是匹配的,倒数第三个字符发生了不匹配的情况。我们把已经匹配的ab叫做好后缀,记做{u}。我们拿它在模式串中进行寻找另一个和{u}相匹配的子串{u*}。那我们就将模式串滑动到子串{u*}和主串{u}对齐的位置。
如果在模式串中找不到另一个等于{u}的子串,我们就直接将模式串,滑动到主串{u}的后面。因为之前的任何一次往后滑动,都没有匹配主串{u}的情况。不过,当模式串中不存在等于{u}的子串时,我们直接将模式串滑动到{u}的后面,这样是否会错过可能匹配的情况呢。如下所示,这里的ab是好后缀,尽管在模式串中没有另一个相匹配的子串{u*},但如果我们将模式串移动到{u}的后面,那就错过了模式串和主串相匹配的情况。