本文主要参考了https://mp.weixin.qq.com/s/rbaPmBejID8-rYui35Snrg的表述,加上部分本身的理解算法
学习任何算法都要了解该算法解决什么问题?咱们看看KMP算法主要解决什么问题。咱们举一个例子,已知字符串1(ABCABBABAABBA)中查找字符串2(ABCAAB)是否存在,若是存在,字符串2在字符串1中的起始位置是多少?学习
对于上面这个列子,咱们很快就能够回答出来字符串2(ABCAAB)在字符串1中存在,起始位置为5(假设从0开始)字体
暴力匹配:ui
不考虑任何效率的问题,固然就是从头至尾比较过去了,好比:spa
ABCABBABAABBA游戏
ABCAAB→字符串
比较下来,发现从第4个字符开始不一样了,那没办法,说明前5个字符不是咱们想要的字串,接下来咱们就让字符串往前拱一步,继续比较:效率
ABCABBABAABBA循环
ABCAAB→方法
这一轮更惨,第一个就栽了,不要紧,继续死磕往前拱:
ABABCBABAABBA
ABCAAB→
这一轮也不咋滴,比较到第3个就歇菜了。只要咱们有毅力,这个过程坚持不懈,总能找到(或者找不到)答案。
暴力求解算法的复杂度为:假设长字符串为n,短字符串为m,在最极端的状况下(只有最后m个字符才互相匹配)咱们须要走n-m轮,每一轮须要匹配m次,时间复杂度为O(nm)
如今的问题是:有没有更快的方法来找到匹配的字串呢?
KMP算法
KMP算法的核心意思就是:当咱们发现一次比较下来字串没有彻底匹配的状况下,下一次的比较也许能够不止往前拱一步,也许能够拱N步,关键是,究竟能够拱几步呢?
搞清楚这个问题前,先来搞清楚一个关键信息:“部分匹配值”,官方的解释太啰嗦了,我掰开了揉碎了讲给你听,就是这样:将咱们要匹配的字串写出来,写两遍:
ABCAAB→
←ABCAAB
看到了吧,咱们让他们一个向左一个向右,相向而行。算一下上一行的右边跟下一行的左边,最多能有几个字符是重合的(注意上一行的首字符不参与这个游戏,不然每一个字串都是从头配到尾,就没意思了),这个数值N就是字串ABAAB的所谓部分匹配值,固然,你会发现此时N等于2,由于:
后缀
ABCAAB→
←ABCAAB
前缀
下边的字串再往前走,再也找不到更多的重合的字符了,所以字串"ABAAB"的部分匹配值为2,记为:
ABCAAB
2
来,继续算部分匹配值,接下来缩短一点,将最右边的B去掉,来看ABAA的部分匹配值:
ABCAA→
←ABCAA
显而易见,字串"ABAA"的部分匹配值是1,记为:
ABCAAB
1 2
不断重复上述过程,将每个字串的“部分匹配值”都算出来,就是所谓的部分匹配表,以下所示。
ABCAAB
00 01 12
好了,花开两朵各表一枝,说回刚刚提到的问题:发现不匹配以后,究竟要向右拱几步呢?答案是:能够往前拱 (N-x) 步。N指的是匹配的字符数,x是部分匹配值,如今咱们再把刚开始的步骤再作一遍:
ABCABBABAABBA
ABCAAB→
比较下来发现有3个字符匹配,此时N=4,查表得知ABCA的部分匹配值x=1,所以咱们此时能够往前拱 (4-1)步,接下来比较:
ABCABBABAABBA
ABCAAB→
我想你们心中必定有这样一个疑问,为何能够这样子移动,这样子移动不会漏掉匹配数据吗?
上面一行的红色字体ABCA的最后一个A其实就是ABCA的最长后缀
ABCABBABAABBA
ABCAAB→
下面一行的红色字体的ABCA的第一个字符A其实就是最长前缀
咱们移动的目的就是要让最长后缀(A)与最长的前缀(A)重合,也就是让两头的部分匹配的区段重合在一块儿
为何能够这样子移动?
由于咱们知道ABCA先后缀匹配部分只有A,也就是说ABCABBABAABBA的前缀AB与ABCA的后缀CA是不匹配的。
若是咱们把小字符串ABCAAB中的A移动到与ABCABBABAABBA中A前面任何一个位置,好比C位置获得的上下字符必定不匹配,由于它们公共部分只有A
ABCABBABAABBA
ABCAAB
你发现,此时咱们就跳过了一些比较循环,让咱们整个算法的效率获得极大的提高。其实KMP算法的核心,就是充分利用比较过的未能匹配的字串的信息,而不是一股脑将他们丢弃。