前言
简要记录下自己在考研期间学习了kmp算法后一些自己的理解。该算法是一种能够在O(m+n)的时间复杂度内完成字符串匹配的算法。其主要的核心依赖于next数组的构建和使用。但是不管是学习的考研视频,还是其他文章之类的很多人对于next数组的问题含糊其辞或者一笔带过,导致我花费了大量的时间在这上面。因此写篇文章简要记录下自己遇到的问题和一些理解。
一、KMP算法是什么
KMP 算法(Knuth-Morris-Pratt 算法)就是一个由三个人提出来的一种高效的字符串匹配的算法。
二、代码详解
1.
代码如下(示例):
注意:当前代码下标是从1开始的,为了方便。如果下标是从0开始,或者是左移的,要进行相应的改变。
next数组的定义:在子串的第j个字符与主串发生匹配失败时,则跳到子串的next[j]位置继续匹配。(我个人感觉也可以理解成,从1到j-1这个子串的最长公共前后缀长度+1)
关于什么是最长公共前后缀,这里就不多解释了,很好理解。
为什么是最长公共前后缀长度+1,因为我们是从下标1开始的,这就相当于把原本的整个数组右移了。如果不+1,那发生匹配失败时,我们会找next【j-1】这个位置,而不是next【j】。这个看自己喜欢。
int sl = s.size(),pl = p.size();//sl是主串,pl是模式串
//匹配开始
int i = 1,j = 1;
while(i < sl && j < pl){
if(j == 0 || s[i] == p[j]){
i++;
j++;
}
else{
j = next[j];//回退到上一个最长相同前后缀长度的位置
}
}
2.如何求next数组
这也是整个算法的核心部分:
分三个部分讨论
1、当j == 0时,此时没有相同前后缀。直接i++,j++即可。即开始下一次的匹配。
2、当p[i] == p[j]时;
next[i+1] = j+1
即next【++i】 = ++j;不理解为什么,再去看一遍上面next数组的定义。
(在这之前前面从1到j-1这个子串和i-1到i-j+1这个子串是完全相等的,要不然也不会轮到第i个字符和第j个字符开始匹配)
3、当p[i] != p[j]时;我当时最大的疑惑就是这里,为什么回退到
j = next【j】;
画个图就好理解了
具体证明可以自行百度
代码如下(示例):
//先初始化next
vector<int> next(pl+1,0);
next[1] = 0;
int i = 1,j = 0;
while(i < pl){
if(j == 0 || p[i] == p[j]){
i++;
j++;
next[i] = j;
}
else j = next[j];
}
总结
!!!