#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
int n, m; // n是模式串p的长度,m是主串s的长度
int ne[N]; // next数组,存储模式串的每个位置的最长公共前后缀长度
char s[M], p[N];// s是主串(长文本),p是模式串(待匹配字符串),下标均从1开始
int main() {
// 输入模式串p(从下标1开始存储)和主串s
cin >> n >> p + 1 >> m >> s + 1;
/************ 构建next数组(核心:模式串自我匹配过程) ************/
// ne[1] = 0,因为单个字符没有真前缀/后缀,直接从i=2开始计算
for (int i = 2, j = 0; i <= n; i++) {
// 当j>0且p[i]与p[j+1]不匹配时,回退j到ne[j]的位置
// 模拟:此时已匹配p[1..j],但p[i] != p[j+1],需缩短前缀长度
while (j && p[i] != p[j + 1]) j = ne[j];
// 如果当前字符匹配,j指针后移,扩展匹配长度
if (p[i] == p[j + 1]) j++;
// 记录当前位置i的最长匹配前缀的结束位置j
ne[i] = j;
// 模拟示例:例如p="ABCABD"
// i=5时,j=2(前缀"AB"),p[5]='A'与p[3]='C'不匹配 → j回退到ne[2]=0
// 然后p[5]='A'与p[1]='A'匹配,j=1 → ne[5]=1
}
/************ 在主串s中匹配模式串p ************/
// i遍历主串s,j表示当前已匹配的模式串长度
for (int i = 1, j = 0; i <= m; i++) {
// 当j>0且s[i]与p[j+1]不匹配时,利用next数组回退j
// 模拟:主串s在位置i与模式串j+1不匹配,需将模式串右移
while (j && s[i] != p[j + 1]) j = ne[j];
// 当前字符匹配,j指针后移,继续匹配下一个字符
if (s[i] == p[j + 1]) j++;
// 完全匹配:当j等于模式串长度n时,找到匹配位置
if (j == n) {
// 计算起始位置:i - n + 1 - 1(因下标从1开始,输出从0开始的索引则需i-n)
cout << i - n << ' ';
// 继续寻找下一个可能的匹配:利用next数组回退j
j = ne[j];
// 模拟示例:主串s="ABABABA", p="ABA"
// 第一次匹配i=3时输出0,j回退到ne[3]=1 → 后续在i=5时再次匹配
}
}
return 0;
}
本文参考了acwing算法基础课。