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         }
原文地址:https://www.cnblogs.com/TheLin/p/13943900.html