zoukankan      html  css  js  c++  java
  • 字符串匹配算法系列一:KMP算法原理

    本文主要参考了https://mp.weixin.qq.com/s/rbaPmBejID8-rYui35Snrg的表述,加上部分自己的理解

    学习任何算法都要了解该算法解决什么问题?我们看看KMP算法主要解决什么问题。我们举一个例子,已知字符串1(ABCABBABAABBA)中查找字符串2(ABCAAB)是否存在,如果存在,字符串2在字符串1中的起始位置是多少?

    对于上面这个列子,我们很快就可以回答出来字符串2(ABCAAB)在字符串1中存在,起始位置为5(假设从0开始)

    暴力匹配:

    不考虑任何效率的问题,当然就是从头到尾比较过去了,比如:

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

  • 相关阅读:
    风雨20年:我所积累的20条编程经验 (转)
    WPF中的Style(风格,样式)(转)
    WPF入门学习(转)
    关于Viual Studio 改变编辑器背景背景及背景图片(转)
    SSIS 控制流和数据流(转)
    修复 Firefox 下本地使用 Bootstrap 3 时 glyphicon 不显示问题
    .net字符串Gzip压缩和base64string转换:
    .net WebClient发送请求实例:
    XmlDocument解析Soap格式文件案例:
    Xml文件操作的其中一个使用方法:
  • 原文地址:https://www.cnblogs.com/techengin/p/8982018.html
Copyright © 2011-2022 走看看