字符串匹配算法系列一:KMP算法原理

本文主要参考了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算法的核心,就是充分利用比较过的未能匹配的字串的信息,而不是一股脑将他们丢弃。

相关文章
相关标签/搜索