相关题目

题目描述

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

模式串 $P$ 在字符串 $S$中多次作为子串出现。

求出模式串 $P$ 在字符串 $S$ 中所有出现的位置的起始下标。

输入格式

第一行输入整数 $N$,表示字符串 $P$ 的长度。

第二行输入字符串 $P$。

第三行输入整数 $M$,表示字符串 $S$ 的长度。

第四行输入字符串 $S$。

输出格式

共一行,输出所有出现位置的起始下标(下标从 00 开始计数),整数之间用空格隔开。

数据范围

$1 \le N \le 10^5$
$1 \le M \le 10^6$

输入样例:

1
2
3
4
3
aba
5
ababa

输出样例:

1
0 2

AC代码

第一种方式

$p$和$s$数组的下标存储都是从$1$开始,每次匹配时候与$j + 1$位置进行比较,算法更易理解和记忆,推荐第一种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

const int N = 100010,M = 1000010;
char p[N],s[M];
int ne[M];
int n, m;

int main(){
cin >> n >> p+1 >> m >> s+1;
//next
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 - j << ' ';
j = ne[j];
}
}
}

第二种方式

此种方式的$p$和$s$数组的下标存储都是从$0$开始,相较第一种方式更难理解,$ne[i]$表示最长前后缀匹配的前缀最后一个字母的下一个字母,这种方式理解更复杂,不推荐,但在特定问题下可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1000010;
int n, m;
char s[N], p[N];
int ne[N];

int main()
{
cin >> m >> p >> n >> s;
//求next数组
for(int i = 1,j = 0; i < m ; i ++) {
while(j && p[i] != p[j]) j = ne[j - 1];
if(p[i] == p[j]) j++;
ne[i] = j;
}
//kmp模式匹配
for(int i = 0,j = 0; i < n; i ++) {
while(j && s[i] != p[j]) j = ne[j - 1];
if(s[i] == p[j]) j++;
if(j == m) {
cout << i + 1 - j << " ";
}
}
return 0;
}