【模板】KMP字符串匹配
题目描述
给出两个字符串 \(s_1\) 和 \(s_2\),若 \(s_1\) 的区间 \([l, r]\) 子串与 \(s_2\) 完全相同,则称 \(s_2\) 在 \(s_1\) 中出现了,其出现位置为 \(l\)。
现在请你求出 \(s_2\) 在 \(s_1\) 中所有出现的位置。
定义一个字符串 \(s\) 的 border 为 \(s\) 的一个非 \(s\) 本身的子串 \(t\),满足 \(t\) 既是 \(s\) 的前缀,又是 \(s\) 的后缀。
对于 \(s_2\),你还需要求出对于其每个前缀 \(s'\) 的最长 border \(t'\) 的长度。
输入格式
第一行为一个字符串,即为 \(s_1\)。
第二行为一个字符串,即为 \(s_2\)。
输出格式
首先输出若干行,每行一个整数,按从小到大的顺序输出 \(s_2\) 在 \(s_1\) 中出现的位置。
最后一行输出 \(|s_2|\) 个整数,第 \(i\) 个整数表示 \(s_2\) 的长度为 \(i\) 的前缀的最长 border 长度。
样例 #1
样例输入 #1
ABABABC
ABA
样例输出 #1
1
3
0 0 1
提示
样例 1 解释
。
对于 \(s_2\) 长度为 \(3\) 的前缀 ABA
,字符串 A
既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 \(1\)。
数据规模与约定
本题采用多测试点捆绑测试,共有 3 个子任务。
- Subtask 1(30 points):\(|s_1| \leq 15\),\(|s_2| \leq 5\)。
- Subtask 2(40 points):\(|s_1| \leq 10^4\),\(|s_2| \leq 10^2\)。
- Subtask 3(30 points):无特殊约定。
对于全部的测试点,保证 \(1 \leq |s_1|,|s_2| \leq 10^6\),\(s_1, s_2\) 中均只含大写英文字母。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
char a[1000005], b[1000005];
int l1, l2, nex[1000005];
int main() {
scanf("%s", a + 1);
scanf("%s", b + 1);
l1 = strlen(a + 1);
l2 = strlen(b + 1);
nex[0] = 1;
for (int i = 2, j = 0; i <= l2; ++ i) {
while (j > 0 && b[j + 1] != b[i]) {
//如果失配,那么就不断向回跳
//直到跳完后的子串后的下一个字符与b[i]相等或j跳到0
j = nex[j];
}
//通过自己匹配自己来得出kmp值
if (b[j + 1] == b[i]) j ++;
nex[i] = j;
}//自己与自己匹配找KMP
for (int i = 1, j = 0;i <= l1; ++ i) {
while (j > 0 && b[j + 1] != a[i]) {
j = nex[j];
}
if (b[j + 1] == a[i]) j ++;
if (j == l2) {
cout << i - l2 + 1 << endl;
j = nex[j];
//注意要跳到nex[j]
}
}
for (int i = 1; i <= l2; ++ i) {
cout << nex[i] << " ";
}
return 0;
}
标签:include,前缀,Luogu,样例,leq,nex,KMP,字符串,P3375
From: https://www.cnblogs.com/jueqingfeng/p/17472938.html