831. KMP字符串
题目链接
831. KMP字符串
给定一个模式串 \(S\),以及一个模板串 \(P\),所有字符串中只包含大小写英文字母以及阿拉伯数字。
模板串 \(P\) 在模式串 \(S\) 中多次作为子串出现。
求出模板串 \(P\) 在模式串 \(S\) 中所有出现的位置的起始下标。
输入格式
第一行输入整数 \(N\),表示字符串 \(P\) 的长度。
第二行输入字符串 \(P\)。
第三行输入整数 \(M\),表示字符串 \(S\) 的长度。
第四行输入字符串 \(S\)。
输出格式
共一行,输出所有出现位置的起始下标(下标从 \(0\) 开始计数),整数之间用空格隔开。
数据范围
\(1≤N≤10^5\)
\(1≤M≤10^6\)
输入样例:
3
aba
5
ababa
输出样例:
0 2
解题思路
KMP
考虑一般的暴力匹配过程,即母串与匹配串一个一个字符匹配,当两个字符不相等时,再移动母串,匹配串从零开始与母串匹配,其实匹配串没必要从零开始,可以考虑之前 \(1\sim j\) 是匹配成功的,先求出这样的 \(next[j]\) 数组,即 匹配串中 \(1\sim j\) 的最长非平凡公共前后缀长度,这样匹配串移动的时候只需要移动到 \(next[j]\) 的位置上,因为 \(1\sim j\) 这部分与匹配串是匹配的,所以关键在于求 \(next[j]\) 数组
- 时间复杂度:\(O(n+m)\)
代码
// Problem: KMP字符串
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/833/
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int n,m,ne[100005];
string p,s;
int main()
{
cin>>n>>p;
cin>>m>>s;
p=' '+p,s=' '+s;
for(int i=2,j=0;i<=n;i++)
{
while(j&&p[i]!=p[j+1])j=ne[j];
if(p[i]==p[j+1])j++;
ne[i]=j;
}
for(int i=1,j=0;i<=m;i++)
{
while(j&&s[i]!=p[j+1])j=ne[j];
if(s[i]==p[j+1])j++;
if(j==n)
{
cout<<i-n<<' ';
j=ne[j];
}
}
return 0;
}