zoukankan      html  css  js  c++  java
  • KMP算法

    KMP算法是现阶段我接触到最难理解的算法,作为暴力匹配算法的改进版,特别是其中涉及到的回溯,很难懂,本篇博客以我的理解记录KMP算法

    一、KMP算法是BF算法的改进,其改进思路为:当模式串与目标串失配时,不回溯到目标串的+1位置,具体怎么做下面说

    举个实例:有目标串“ABCDABCDABDE”和模式串“ABCDABD”进行匹配,当模式串”ABCDAB“和目标串的“ABCDAB”匹配完成,但'C'不等于’D‘ 

    此时第一个字符匹配失败,按照BF算法,应该将模式串移到目标串的第二个位置进行比较,但是这次肯定是失败的,因为第二个位置在之前比较时已经

    知道它和模式串中’B‘匹配,而’A‘不等于’B‘ 所以一定失败,这就是有用信息!

    而如何知道’A‘不等于’B‘则是我们要求的

    二、Next数组,前缀后缀和最长公共元素

    如果给定的模式串是:“ABCDABD”,从左至右遍历整个模式串,其各个子串的前缀后缀分别如下表格所示:

    可以看到,一个字符串前缀就是以第一个字符开头,不断往后”拉“的一个集合

    后缀就是以最后一个字符结尾,不断往前”伸“的一个集合(用语言实在难以表述,不过对于Next数组理解这很重要)

    最大公共长度就是两个集合公共元素的长度。

    对于上表有

     对于该表的一点解释:字母下面的数字表示一个字符串(第一个字母到该字母之间的所有字母)的最大公共长度

    如B下标为2,表示字符串”ABCDAB“的公共元素”AB“长度为2

     这个便是我理解的next数组。

    可以看出,next数组不会突然增加,这是由next数组的性质决定的,且next[i]可以由next[i-1]推出

    还是以字符串s:”ABCDABD“为例,设置一个变量k用来跟踪next的值,现假设B的最大公共元素未知,即next[5]不知道,即

     而我们知道此时k=1,此时next[5]的值要根据s[5]的值来决定

    1.s[k]=s[5],即”AB“="AB",此时字符串”ABCDAB“最大公共长度为2,即next[5]=next[4]+1 同时k应该+1

    此时next数组为[0,0,0,1,2,?]

    这是匹配的情况,对于不匹配的情况怎么办呢?下一个字符'D'就是这种情况(此时k为3)

    2.s[k]!=s[6],即”C“和”D“失配了,那么此时”ABCDABD“有没有最大公共长度呢?

    如果要有,则必须要满足一个条件:在k前面必须要有”D“字符!!!即”xxDxxxD“才有可能出现公共前后缀”xxD“

    即现在要做的:找到k'使得s[k']='D'即可,k’=next[k-1](核心!!!!!!)

    要理解next[k-1]是什么,next[k-1]是s[k-1]的最大前缀!!

    即s[k]!=s[6]的时候,应该在k-1的最大前缀里面找有没有字符‘D’

    此时不必担心s[k']到s[k]之间存在‘D’ 就算存在也不是我们要找的字符‘D’

    (这里并没有搞懂,但是我构建了许多字符串都找不到符合要求的字符串,构建过程中我感觉这和前后缀的性质有关系,但是涉及的知识太深了无法进一步求解,这可能需要很高的数学造诣才能弄懂)

    这就是我理解的KMP算法,对于其中核心并没有弄太懂,但是其构造方法和核心递归k=next[k-1]弄懂了一点,也算是有了收获

    完整代码如下:

     1 public static int kmpSearch(string str1, string str2,int[] next)
     2         {
     3             //遍历
     4             for(int i=0,j=0;i<str1.Length;i++)
     5             {
     6                 //处理str1[i]!=str2[j],调整j大小
     7                 while(j>0&& str1[i]!= str2[j])
     8                 {
     9                     j = next[j - 1];
    10                 }
    11                 if (str1[i]==str2[j])
    12                 {
    13                     j++;
    14                 }
    15                 if(j==str2.Length)
    16                 {
    17                     return i - j + 1;
    18                 }
    19             }
    20             return -1;
    21         }
    22         //获取一个字符串的部分匹配值
    23         public static int[] kmpNext(string dest)
    24         {
    25             //创建一个数组保存部分匹配值
    26             int[] Next = new int[dest.Length];
    27             //dest的第一个字符串部分匹配值为0
    28             Next[0] = 0;
    29             for (int i = 1, j = 0; i < dest.Length; i++)
    30             {
    31                 //当dest[i] != dest[j]时
    32                 while (j > 0 && dest[i] != dest[j])
    33                 {
    34                     j = Next[j - 1];
    35                 }
    36                 //该条件满足 部分匹配值就+1
    37                 if (dest[i] == dest[j])
    38                 {
    39                     j++;
    40                 }
    41                 Next[i] = j;
    42             }
    43             return Next;
    44         }
  • 相关阅读:
    struts2 标签
    Ubutu命令 笔记积累
    虚拟机VirtualBox和Ubutu
    更新PostgreSQL数据
    向PostgreSQL插入数据
    PostgreSQL表依赖性跟踪
    PostgreSQL外部数据
    PostgreSQL分区介绍
    PostgreSQL继承详解
    PostgreSQL模式介绍
  • 原文地址:https://www.cnblogs.com/TheLin/p/13943900.html
Copyright © 2011-2022 走看看