文章目录
一. 什么是KMP
KMP是利用公共前后缀进行移位匹配的匹配算法。在失配时固定主串指针i (i无需回溯),依据Next[]将模式串向后移动与匹配指针对齐
对比朴素暴力匹配法O(nm),KPM匹配更高效O(n+m)
实现过程:
二. 为什么可以移位?
从公式推导来说
从直观来说
为了方便叙述,声明了几个名词
把模板T在指针j之前字符构成的子串,姑且先称之为“已配对模式子串”,把已配对模式子串的开头前k-1项,和j前k-1项,称为模板j前子串的“前缀”和“后缀”
已知主串Si的前x项已经和“已配对模式子串”完全相等, 在第i项时Tj与Si相异, 现在考虑移位问题。
既然i前面部分已完全相等,Si的前x项中每个位置都已经和“已配对模板子串”的对应位置一一对应了(此时“已配对模板子串”的“前后缀”,也是Si前的前x项的前后缀 ),如果一个一个移位的话大多情况下都是在“已配对模式子串”开头部分就已经无法对应,但是有一种特殊情况除外 — 当“前缀”等于“后缀” 时,将“已配对模式子串”的“前缀” 直接 移到主串S的“后缀”对应位置,以此达到了快速移位,减少无意义匹配的目的
综上所述:移位的k只与模式T自身相关(与模式T的公共前后缀相关)
三.怎么实现KMP
对移位规则的实现:Next[]
模式T的移位是为了前后缀匹配
[一句话总结] 实现KMP的算法流程:判异同 和 移位找前缀
就干两件事情:判异同 和 移位找前缀
因为构建Next数组本质上也是使用KPM匹配(主串和模式都为原模式T),所以为了更普适Next的实现,调整了一下KPM和Next数组的实现流程图
1.KMP实现: Si,Tj位判异同,相等时S,T同进位,相异时T移位进行前缀匹配,看能否最终匹配全部字符串
2.构建Next[]:找Next[j+1],就是Tj,Tk判异同,看能否扩充T[1, j]任一子前缀 (移位找子前缀)
找Next[j+1],就是Tj,Tk判异同,相等时返回原前缀长+1(等效于扩充了前缀),相异时移位往前找T[1, j]所有子前缀,最终获得每个j上的Next[j]值
tip:扩充前缀(即前缀后一位Tk与Tj相等)
具体代码实现和解释
#include<bits/stdc++.h>
using namespace std;
void get_next(string T, int next[]){ // T中T[0]储存的是T的字符串长度
int i = 1, j = 0; // i为T中指针,j为next数组下标
Next[1] = 0;
while(i < T[0]){ // j即保存了Ti配位的位置,也通过 j-1 保存了最大公共子串的字符数
if(j == 0 || T[i] == T[j]) {i++;j++;Next[i] = j;}
else j = Next[j];
}
}
int KMP(string S, string T, int pos){ // T中T[0]储存的是T的字符串长度
int i = pos; int j = 1;
while(i <= S.length && j <= S.length){
if(j==0 || T[j] == S[i]) {++i; ++j;}
else j = next[j];
}
if(j > T[0]) return i - T[0];
else return 0;
}
int main(){
return 0;
}
附:修正后的Next代码
#include<bits/stdc++.h>
using namespace std;
int* getNext2(int Next[], string T){
int j = 0; Next[1] = 0;
int i = 1;
while(i < string.length()){
if(j = 0 || T[i] == T[j]) {
j++; i++;
if(T[i] != T[j]) Next[i] = j;
else next[i] = next[j]; // 等于前一位
}
else j = Next[j];
}
}
四. 例题上手实操
class Solution {
public:
void getNext(int* next, const string& s) {
int j = 0;
next[0] = 0;
for(int i = 1; i < s.size(); i++) {
while (j > 0 && s[i] != s[j]) {
j = next[j - 1];
}
if (s[i] == s[j]) {
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
if (needle.size() == 0) {
return 0;
}
int next[needle.size()];
getNext(next, needle);
int j = 0;
for (int i = 0; i < haystack.size(); i++) {
while(j > 0 && haystack[i] != needle[j]) {
j = next[j - 1];
}
if (haystack[i] == needle[j]) {
j++;
}
if (j == needle.size() ) {
return (i - needle.size() + 1);
}
}
return -1;
}
};