大家好,我是一个每天在互联网都被读者催更催到爆肝,爆肾小鹿童鞋。
说实话,一些数据结构和算法我这辈子都不可能用到实际当中,但个人一直觉得能把复杂的东西讲明白是一件很牛逼的事情。
毕竟想牛逼也是很难的,并不是我说了算,前几天更新的的 BF 和 RK 算法,就被后台小伙伴的留言疯狂石锤,哼!你牛逼你就讲讲 KMP 算法,我要石锤。
这几天吓得俺吃饭吃不消,睡觉睡不香,干啥啥不行,这无数的与 KMP 战斗的夜晚。今天他来了,他带着 KMP 来了。讲就讲,今天不讲明白你们谁都别想走,放学都在学校门口等着。
(为了能够让大部分读者对 KMP 的基本原理,文中不具体分析代码实现)
听说 KMP 算法是一种字符串的改进算法,嗯~ 字符串匹配算法还有啥?不好意思,记性不好,居然忘记了。咱们继续,KMP 算法是由D.E.Knuth,J.H.Morris 和 V.R.Pratt 发明的,至于名字怎么读,四级没过,反正我不认识。
这都不重要,重要的是 KMP 算法的核心,它是主要利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。
嗯~ 听起来比暴力破解法牛逼的多,至于如果快速匹配的,还需要深入研究一下。
在一个夜黑风高的夜晚,我翻看了全网的 KMP ,读了一遍又一遍,还是看不懂,不知吃了多少碗泡面之后,突然灵感来了,
如果由以上模式串和子串,子串要在模式串中做一个字符串匹配,一般的匹配过程如下:
当我们遇到最后一个字符匹配 E 与 D 并不能完全匹配,那么我们开始往后移动子串一个,一个个重新进行比较。
然而发现第一个字符就不能匹配成功,那好吧,继续往后移动子串。
直到移动到下面这种情况,虽然匹配了 A 和 B,但是到了第三个字符,E 和 C 又不能匹配了,真伤心,好不容易匹配成功前两个。
通过以上,我们发现暴力匹配,就算前几个已经匹配成功,但是由于最后匹配失败导致了重新移动一位,这也太浪费时间了。
既然我们知道前几位匹配的信息。比如第一次匹配,我们知道了已经成功匹配了 ABCAB 了。能不能利用已经知道的匹配信息进行析,然后一次性往后移动多位呢?
实话告诉你,没有。但是这几个人做到了,分别是 K 某,M 某,P某的三个人,不愧是大佬中的传奇人物,我就咋没想到呢?这次又被石锤智商低下了。
但是我们想象能力还是挺强的,假如有一个神奇的“表”,我们暂且起名叫做匹配表吧,那么我们每次移动的次数,这个神奇的表能够告诉我们,我们岂不是美滋滋的往后移动多位进行快速匹配不就可以了吗?
尼玛尼玛哄,你骂谁呢?不好意思,错了,重来,玛尼玛尼哄,这张神奇的匹配表说来就来。
虽然这个表目前是幻想出来的,无关紧要,我们再试着匹配一次上边用到的字符串。
开始正式匹配,匹配到 E 和 D 就卡壳了,既然有了神奇的匹配表,不需要一个个的移动了,我们应该问一下神奇的“表”,魔表魔表,下一步应该把子串移动多少位?
此时匹配表进行疯狂的计算,对于如何计算,也告诉我们公式了。它来询问你,这才字符匹配成功匹配了几个字符,很容易在图中得到,ABCAB 共 5 位匹配成功。
然后又问,这五位中最后一位是谁?当然是 B 了,那么 B 在匹配表中的匹配值是多少?稍等,我看看匹配表哈~ 嗯~ 这个 B 对应的是 1。
好了,用成功匹配的位数减去匹配值就是下一次要移动的次数。
纳尼?这么神奇?然后我就傻了吧唧相信了去试了试,测试过程如下。
嗯~ 5 - 2 等于 3,所以我要将子串移动四位,如下图位置。
然后我再进行匹配,当遇到 E 与 C 的时候,匹配失败,此时已经匹配了两个元素,分别为 AB,那下一次要往后移动多少呢?
还是按照上边的方式计算,已匹配 2 的字符,此时已匹配字符 AB 最后一个字符是 B,所以这个 B 对应的匹配表的匹配值为0,往后移动 2-0等于 2,往后移动两位。
继续匹配,发现第一个字符就匹配失败了,继续移动一位,最后发现全匹配。
整体的 KMP 算法就是这么一个思路,但是我们所用到的这个匹配表示我们想象出来的,能不能计算出来呢?理想总是很美好,但现实很骨感。
首先要弄懂两个概念,就是前缀和后缀的概念。
对于我们的子串来说,它的前缀有哪些呢?对于前缀就是除了最后个字符以外所有的顺序组合方式。比如 A、AB、ABC、ABCA、ABCAB。
后缀正好相反,除了第一个字符外,其他所有的组合方式。比如BCABD、CABD、ABD、ABD、BD、D。
对于每个匹配值是如何计算的,那就对子串的每个字符组合寻找出前缀和后缀,然后进行比较是否有相同的,相同的字符组合有几位,就是所谓的匹配值。
这样纯文字说可能很懵逼,直接上图。
我们可以在图中发现,并没有对子串移动的规律发现任何问题,但是我们在看一下子串。
突然恍然大悟,从上图中发现有时候在同一字符串中出现两种相同的字符,比如上图中的 AB,由两个字符组成的,所以匹配值为 2,同时在匹配的过程中,如果 D 前边的字符能够匹配,则子串往后移动 3 位,则再次匹配 AB,原来这是 KMP 的精髓所在。
文章整理了好几个小时,不妨点赞支持一下。嘻嘻,你不点赞说明你很自私,你怕那么好的文章让别人也看到。开个小小玩笑。
1、老铁们,关注我的原创微信公众号「小鹿动画学编程」,专注于数据结构与算法、网络原理、Web 大前端。
2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我,嘻嘻。