最近朋友面试京东C++研发岗实习生时,被问到KMP算法的实现原理。
KMP算法很多人都会忽略,可能只是听过这个名字罢了,没有真正的学习过,这个算法相对来说比较难理解,今天我参考了很多KMP算法的文章博文,加以优化,撰此博文,分享一下自己的通俗理解。
1、要解决的问题
BF算法、KMP算法要解决的问题相同,即:给出两个字符串A和B,求解A中是否包含B?如果包含,求出包含了几个?
![1114a5f0af619bab68eff9e8721b5a7f.png](https://i-blog.csdnimg.cn/blog_migrate/3d08ff6674123a6324f6e3486d0aec3e.png)
2、BF算法
针对上面的问题,如果让我们用眼睛找,那么我们大脑使用的就是BF算法,即:一位一位地比较,比较到失配位的时候,将原本指向A串第六个字符的指针
![2b861068b0c546d5798bc55c9b9bc2f5.png](https://i-blog.csdnimg.cn/blog_migrate/216f824370181a2100263006cfcc9aec.png)
![69f39eaa382ee75066834822ce6e0631.png](https://i-blog.csdnimg.cn/blog_migrate/04ba9a8b8960ed8de8052d986554048b.png)
此时要比较的第一位就不相等,即此轮匹配也是必然失败的,但计算机并不知道,它只会按照BF算法一位一位的比较下去(在很多情况下要比较很多位才能发现不匹配),这种暴力求解的算法效率是极低的。
为了让计算机根据已经匹配过的部分知道自己从头匹配的时候应该忽略哪些部分,省去不必要的匹配?(在此例中即为从头跳过第二位的b从第三位开始新的匹配)KMP算法就是用来解决这个问题的。
3、KMP算法
3.1先验概念了解一下
前缀、后缀、公共最大长度next、部分匹配表(Partial Match Table)
abcdef的前缀:a、ab、abc、abcd、abcde(注意:abcdef不是前缀)
abcdef的后缀:f、ef、def、cdef、bcdef(注意:abcdef不是后缀)
abcdef的公共最大长:0(因为其前缀与后缀没有相同的)
ababa的前缀:a、ab、aba、abab
ababa的后缀:a、ba、aba、baba
ababa的公共最大长:3(因为他们的公共前缀后缀中最长的为aba,所以长度为3)
引自: https://www. zhihu.com/question/2192 3021/answer/642165149
部分匹配表:
![a75652e3383a6c1027b2170c0464a80a.png](https://i-blog.csdnimg.cn/blog_migrate/85391ce6503589e015fffea507426a9c.png)
index行是索引。
字符串B行则记录了所有字符。
next行则记录了当前从B串头部到当前位置的这一子串的公共最大长。
new行记录的值则是(next-1)
3.2步骤
核心:用成功匹配的位数减去匹配值就是下一次要移动的步数。
- 字符串A和B从头开始一对一进行比较,直到在index=5处开始不等,在匹配表中找到index=4(一定要找不等处的index-1)时的公共最大长next,此时next=2,
- 重新进行第二轮比较的时候可以直接将B串的头部2个字符和A串匹配成功的部分的最后两个字符对齐。然后开始对比B串的第三个字符与A串的失配字符,进行新一轮的匹配。
- 关于对齐,我们在匹配时分别用指针
和
指向字符串当前匹配的位置,失配之后
![509de5db50574417b30bebe930e4bd31.png](https://i-blog.csdnimg.cn/blog_migrate/adb61e9b14c183707ead34e143814ea4.png)
黄色的ab即是通过公共最大长直接匹配的位置,红色部分是重新开始匹配的位置(两个指针直接指向的位置),相较于BF算法,我们在这一步跳过了A串的第二个字符“b”,第三个字符“a”,直接将B串头部对齐了第四个字符,并从B串的第三个字符开始重新与此前失配的字符进行新一轮的匹配,这样设计新一轮匹配的过程省去了一大堆不必要的匹配。
public