前言
目前是写给自己看的,,仅供参考
左神的KMP代码
#include<iostream>
#include<string>
using namespace std;
int* getNextArray(string str2){
if(str2.size() == 1){
int* next = new int[1];
next[0] = -1;
return next;
}
int* next = new int[str2.size()];
next[0] = -1;
next[1] = 0;
int i = 2;
// int cn = 0;
int cn = next[i-1];//被比较对象的下标位置
while(i < str2.size()){
if(str2[i-1] == str2[cn]){
next[i++] = ++cn;
}else if(cn > 0){
cn = next[cn];
}else{
next[i++] = 0;
}
}
//退出循环意味着数组已经填完了,
return next;
}
int getIndexOf(string str1, string str2){
if(str1.size() == 0 || str2.size() == 0)
return -1;
int i1 = 0;
int i2 = 0;
int* next = getNextArray(str2);
while(i1 < str1.size() && i2 < str2.size()){
if(str1[i1] == str2[i2]){
i1++;
i2++;
}else{
//当i2已经到了next的0位置,此时next【0】 值为-1,so 不用显示的指定i2=0了;
if(next[i2] == -1){
i1++;
}else{
i2 = next[i1];
}
}
}
return i2==str2.size()? i1-i2:-1;
}
int main(){
cout << getIndexOf("abcabcd","cab");
return 0;
}
以上两个函数,一个是求解next函数,一个是使用next函数
getIndexOf()函数
利用现成的next数组来判断,有以下情况(i和j代替代码中的i1和i2)
首先i指向string,j指向pattern,string[i]和pattern[j]如果相等则两个都加一,很直观;
如果string[i]和pattern[j]不等,则把j指针跳到前面的next[i]位置,如果j指向的是第一个pattern字母,即next[j]==-1,则i直接加一就好了,
next数组的求解
next数组长度是和pattern一样的,含义是:数组里每位i代表着0到i-1的串的最长匹配前后缀(这词用的可能不准确,看例子理解),
首先人为规定第0位为-1,第一位为0,第二位开始计算,代码也是很精简的,两种情况:
cn是上一个字符串的最长前缀的最后一位的索引,同时也代表着最长前缀长度
1. 如果cn指向的值等于当前的下一位,则下一位的next值是现在的长度+1,这个好理解
2.如果cn的值不等于当前的下一位,则下一位的next值是next[cn],也就是最长前缀的最后一位,它对应的最长前缀,为什么呢,这个证明就没那么直观了,以后再弄TODO
TODO上面这些有点瑕疵,以后再来修改2020-5-5,
---
代码写起来很简单,记住这几种情况就好,特别是cn>0的情况,让cn=next[cn]比较难理解要记住
============
知乎有人说网上KMP的实现又很多种,眼花缭乱的,轮子哥推荐了一本<柔性字符串匹配>,评论说"单拿KMP算法来讲,这是我见过的讲解最清晰明了的书了!",设为TODO,以后有空看吧...而这篇的代码是从左神课里选的
KMP的算法复杂度
目前没找到证明,但是网上说是O(M+N),算法第四版里只写了运算次数
推荐阅读:
如何更好地理解和掌握 KMP 算法? - 海纳的回答 - 知乎 https://www.zhihu.com/question/21923021/answer/281346746