831. KMP字符串


题目链接

831. KMP字符串

给定一个模式串 \(S\),以及一个模板串 \(P\),所有字符串中只包含大小写英文字母以及阿拉伯数字。

模板串 \(P\) 在模式串 \(S\) 中多次作为子串出现。

求出模板串 \(P\) 在模式串 $S4 中所有出现的位置的起始下标。

输入格式

第一行输入整数 \(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 
 
//#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 PII;
typedef pair PLL;
 
template  bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template  bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template  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<