核心思想:当子串和母串不匹配时,直接让子串下移到下一次可以和前面部分字符串匹配的地方,节省了中间无效的匹配时间
#include<iostream>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int n, m;
char p[N], s[M];
int ne[N];
int main()
{
cin>> n >> p + 1 >> m >> s + 1;//字符串的下标从1开始,所以从p+1开始读入
//计算前缀数组
for(int i = 2, j = 0; i <= n; i ++ ){//s[1]的前缀数组为0,所以直接从2开始匹配
while(j && p[i] != p [j + 1]) j = ne[j];//当p数组和子串不匹配时,子串直接移动到下一次可以和前面部分字符串匹配的位置
if(p[i] == p[j + 1]) j ++ ;//当子串数组的下一位和p数组的下一位匹配时,j++代表前缀后缀数组相同部分的长度++;
ne[i] = j;//字符串第i位的kmp值为j;
}
//字符匹配
for(int i = 1, j = 0; i <= m; i ++ ){
while(j && s[i] != p[j + 1])j = ne[j];//当s数组和p数组的下一位不匹配时,p数组直接移动到下一次可以和前面部分字符串匹配的位置
if(s[i] == p[j + 1]) j ++ ;//当s数组和p数组匹配时,j++(j此时代表匹配的长度)
if(j == n){//当匹配的长度等于p字符串的长度时,代表全部匹配成功
cout<<i - n<<" ";//输出起始位置,即当前位置减去p字符串长度
j = ne[j];//j移动到下一次可以和前面字符串匹配的位置
}
}
return 0;
}