详解请点击如下链接:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
void getNext1(string t,vector<int>& next) {
int i = 0, k = -1;
next[0] = -1;//第一个字符前无字符串 赋值-1
//遍历字符串比较前后缀
while (i < t.size()-1) {
if (k == -1 || t[i] == t[k]) {
next[++i] = ++k;//对应字符匹配情况下,s与t指向同步后移
}
else {
k = next[k];
// 我们现在知道next[k]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
// 也表示该处字符不匹配时应该回溯到的字符的下标
// 这个值给k后又进行while循环判断,此时t[k]即指最长相等前缀后一个字符
}
}
}
//改进
//为什么KMP算法这么强大了还需要改进呢?
//大家来看一个例子:
//主串s = “aaaaabaaaaac”
//子串t = “aaaaac”
//这个例子中当‘b’与‘c’不匹配时应该‘b’与’c’前一位的‘a’比, 这显然是不匹配的。'c’前的’a’回溯后的字符依然是‘a’
void getNext2(string t, vector<int>& nextval) {
int i = 0, k = -1;
nextval[0] = -1;//第一个字符前无字符串 赋值-1
while (i<t.size()-1) {
if (k == -1 || t[i] == t[k]) {
if (t[++i]==t[++k]) {//当两个字符相等时要跳过
nextval[i] = nextval[k];
}
else {
nextval[i] = k;
}
}
else {
k = nextval[k];
// 我们现在知道next[k]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
// 也表示该处字符不匹配时应该回溯到的字符的下标
// 这个值给k后又进行while循环判断,此时t[k]即指最长相等前缀后一个字符
}
}
}
//KMP算法
int KMP(string s,string t) {
vector<int> next(t.size());
getNext2(t, next);
int i = 0;//主串位置
int j = 0;//模式串位置
while (i < (int)s.size() && j < (int)t.size()) {
if (j == -1||s[i] == t[j]) {
i++;
j++;
}
else {
j = next[j];//j回到指定位置
}
}
return j>=t.size()?i-t.size():-1;
}
int main() {
string s, t;//主串 模式串
cin >> s >> t;
cout << KMP(s, t);
return 0;
}