KMP算法记录

参考网址:

基本概念

以" abcde "为例子

  • 1、前缀字符串
    • 前缀字符串,即 a、ab、abc、abcd、abcde
    • 其中最长前缀字符串为 abcde
  • 2、后缀字符串
    • e、de、cde、bcde、abcde
    • 最长后缀字符串为 abcde

算法演变

提前约定:主串用 mainStr 表示,子串用 patternStr 表示

这里mainStrin假设为" aaaaaaaaab ",patternStr设为" aaaab "

  • 1、最初的算法:BF算法 暴力搜索
    • 所谓暴力搜索,就是从主串mainStr与子串patternStr一个一个比对,不满足重新来
    • 暴力搜索会出现“回溯”问题
    •   {
          for(int i=0;i<mainStrLen;i++){ //主串mainStr遍历
              for(int j=0;j<patternStrLen;j++){ //子串patternStr遍历
                  /**
                   * 主串从 i 的位置,同子串一同向后移动挨个比较
                   * 如果有一个不相同,则跳出:言外之意就是,主串从 i+j 的地方回溯到原来 i 的位置
                   * 之后 i++ 向后移动一格,重新继续挨个比较 (所以意味着,上一回合 i+j 挨个比较的
                   * 时候,主串 i++ 的位置已经被比较过了,甚至后面的也被比较过了,现在又要从i++的位置
                   * 重新挨个比较,这就是无用功的地方)
                   */
                  if(mainStr.charAt(i+j)!=patternStr.charAt(j)) break;
      
                  /**
                   * 如果比对完了patternStr子串,说明匹配成功
                   */
                  if(j==patternStrLen-1) return i;
              }
          }
          return -1;
        }
      
  • 2、KMP算法要解决BF算法中的点
    • 解决BF算法指针回溯问题
    • 解决办法:利用patternStr当前匹配位置之前的字符串的不包含自身的 最长前缀字符串 与 最长后缀字符串 相同的情况,跳过 最长前缀字符串 位置,来到它的后一个位置位置,直接去匹配不相同的部分(这个部分详情看顶部哔哩哔哩连接,有动画讲解)

    例如 主串为ABxxxABCxxxxxx,子串为ABxxxABE,当比对到主串字符 C 和子串最后一个字符 E 的位置时,这里说的“patternStr当前匹配位置之前的字符串”表示为E之前的“ABxxxAB”,它的不包含自身的 最长前缀字符串 为 AB ,最长后缀字符串为后面的 AB,所以当E这个位置与C不相同时,下一次比较就只需要从主串当前匹配位置 C ,继续和子串刚刚的 最长前缀字符串AB 之后的位置开始比较(就是AB之后的xxx的第一个,这里称为跳转位置,即最长前缀长度+1),这样主串不需要回溯,子串也不需要从头开始挨个对比。而这里有一个点就是,凭什么主串不回溯,ABxxxABC之间的xxx可以跳过?万一他们恰好满足整个字符串匹配怎么办。这个还是可以看那个哔哩哔哩视频里的讲解。只要保证 最长前缀字符串 与 最长后缀字符串 确确实实是刚刚那两个 AB,那么他们就一定可以跳过,因为如果xxx中有满足匹配的情况,则刚刚说的AB就不可能是最长前缀字符串与最长后缀字符串。

    • 得到结论:
      • 引导出Next数组:因此通过上面,可以得到一个结论:子串每一个位置都可以有一个跳转位置,使得这个位置不匹配时,可以调到某个位置继续与主串位置继续匹配(包括回到子串开头位置),因此可以把这些跳转位置写成数组,与子串每一个字符一一对应,这个数组就是 Next 数组。每一次遇到不匹配时,就通过这个数组来获得下一个比较位置
      • Next数组和主串其实没有任何关系,任何子串一旦固定,那么其对应的Next数组就固定了。因为是最长前缀字符串和最长后缀字符串只取决于子串(其实很好理解,仔细想想,为什么要弄这两个前后缀字符串来做参考,就是因为他俩一个是字符串开头,一个是字符串末尾,并且相等,所以子串就不需要回到开头了,只需要到跳转位置就好了)。

KMP算法重点难点

  • 如何求解 Next数组?吐槽一句,其实在子串里找最长前缀字符串和其对应的最长后缀字符串,本身就是一个匹配,这个算法真反人类
  • 求解过程:参考顶部的第二个网址
  • 这里是源码下载地址 https://files.cnblogs.com/files/blogs/668717/searchAlg.zip
posted @ 2021-08-12 22:00  麦块程序猿  阅读(68)  评论(0编辑  收藏  举报