KMP算法解决的是字符串匹配的问题
文本串:aabaabaaf
模式串:aabaaf
判断文本串中是否存在模式串
需要用到前缀表:
前缀:包含首字母不包含尾字母的所有子串
后缀:包含为字母不包含首字母的所有子串
求最长相等前后缀
初始化
处理前后缀不相同的情况
处理前后缀相同的情况
更新next数组
i:指向后缀末尾位置
j:指向前缀末尾位置,还代表i包括i之前子串的最长相等前后缀长度
初始化:j=0;注意这个位置对j的初始化其实有好几种方式,有的人习惯于赋值-1,也就是对整个next数组进行-1操作;有的习惯于使用原next数组,我比较倾向于next原数组,这二者的差别只在于进行j回溯的时候那句逻辑。
next[0]=0;
然后循环里对i进行初始化,循环里头,先判断后缀不相同的情况:不相同的话要对j进行回溯,回溯到前一位对应的回退位置;
如果前后缀相同,将j++,然后更新next数组。
这样就完成了next数组的构建。
然后进行匹配的时候,两个字符串用两个下标分别进行迭代,注意模式串的下标初始值跟next数组起始位置一样,习惯于初始化为0,这也是我为什么倾向于next数组初始化为0的原因;
还是先处理不匹配的情况,进行回溯;
匹配的话移动;
根据不同题目判读不同终止条件。
class Solution { public: void getNext(int* next, const string& s) { int j = 0;//前缀的末尾,统一0操作,所以初始化为0 next[0] = j; for(int i = 1; i < s.size(); i++) { //先处理前后缀不相等的情况,如果不相等,前缀要进行回退,知道回退到相等或者到模式串开头位置,回退的下标就是当前前缀前一个位置的next数组的值 while(j > 0 && s[i] != s[j]) { j = next[j-1]; } //处理前后缀相同的情况,如果相同,i和j都往后移一位,j++,next[i]等于j if(s[i] == s[j]) { j++; } next[i] = j; } } int strStr(string haystack, string needle) { //haystack是文本串 //needle是模式串 //KMP算法,需要构造next数组 if(needle.size() == 0) { return 0; } int next[needle.size()]; getNext(next, needle);//得到前缀表 //得到前缀表之后,需要进行匹配,用j来迭代模式串,用i来迭代文本串 int j = 0;//因为next数组里是从0开始的 for(int i = 0; i < haystack.size(); i++) { //先处理二者不相等的情况,如果不相等,模式串需要回退,按照next数组进行回退 while(j > 0 && haystack[i] != needle[j]) { j = next[j-1]; } //如果二者相等,继续向后判断 if(haystack[i] == needle[j]) { j++;//i++在循环里头 } //判断是不是模式串在文本串里头的条件:迭代器j已经到了尾部 if(j == needle.size()) { return i - needle.size() + 1; } } return -1; } };