KMP算法字符串匹配算法:
KMP算法匹配图解
如下图p在t中找到可以匹配的下标:
常规:我们常规做法都是暴力匹配,此处我们不介绍暴力匹配。
KMP的做法:
此时下标为5处发生不匹配,首先我们先介绍KMP如何进行匹配,稍后在介绍next怎么得到的
这时,看到我们将p下标为3的位置再和T下标为5的位置进行匹配,也就是T下标为5处不匹配时,需要看next数组前一个索引(4)处的元素是多少,可见next[4]的元素是3 则P下标为3元素与T下标为5的元素进行匹配。接着在看到T的下标为7的位置又发生了不匹配现象
此时你大概知道KMP的算法流程了。
我们再来看最后一个例子:
此时看到下标为4的地方发生匹配错误,根据P下标4前一个(3)元素对应的next元素为1,则将P数组下标为1处与T的下标为4处进行匹配。
此时发现T的索引为5的位置,发生了不匹配现象.
此时,我们看T索引5对应匹配数组P的索引为2,则我们看2的前一个位置P数组索引为1,对应的next的元素是多少,此时发现元素为0,为0时,我们的P数组不对T下标为5处的位置进行匹配,数组P之间后移从T的索引为6处进行匹配。
最终匹配完成。,总结一下匹配过程,若T的i处和P的j处发生不匹配,接下来我们看P的j-1处的next元素是多少,next[j-1]处的元素,为P的匹配索引,再跟T的i处进行匹配,若还是不匹配,则再接着回跳,也就是再看 P前一个索引的next元素。
接下来我们介绍一下 P的next数组是怎么来的:
如图next数组是这样得到的。
我们来模拟一下next数组的生成
#include<iostream>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int n, m;
char p[N], s[M];
int ne[N]; // next数组
//next数组的生成
void getNext() {
for(int i = 2, j = 0; i <= n; i++) {
/*
i代表 前缀子串末尾, j代表后缀字串的末尾,也代表 next数组的元素
前后缀不相同时,我们此时根据前一个next[j],来找前一个前缀子串,进行比较,
也就是 j进行回退的过程,直到 j ==0 或者 p[i] == p[j+1]; 退出 j的回退
*/
while(j > 0 && p[i] != p[j + 1]) j = ne[j];
/*
前后缀相等时,j向后移动一位
*/
if(p[i] == p[j + 1]) j++;
ne[i] = j;
}
}
int main() {
cin >> n >> p + 1 >> m >> s + 1;
getNext();
//KMP 的匹配过程
for(int i = 2, j = 0; i <= m; i++) {
while(j > 0 && s[i] != p[j + 1]) j = ne[j];
if(s[i] == p[j + 1]) j++;
if(j == n) { // j == n则找到 匹配的子串
cout << i - n + 1 << '\n';
}
}
// next数组的值;
for(int i = 1; i <= n; i++) {
std::cout << ne[i] << " ";
}
std::cout << '\n';
return 0;
}