算法基础——KMP字符串匹配

时间:2022-01-09 04:32:03

原题链接

题目:

给定一个模式串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

解题代码:

#include <iostream>
#include <cstdio> using namespace std;
const int N = 100010, M = 1000010;
char p[N], s[M];
int ne[N];
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]; //前缀与后缀匹配失败,使j回退到之前找到的公共前后缀的长度的位置,直到 p[i] 和 p[j + 1]匹配 或 j 回退到了起点(0的位置)。
if(p[i] == p[j + 1]) j++; //前缀的i下标元素和后缀的j+1下标元素匹配成功,并继续匹配下一个
ne[i] = j; //前i个数中的最长公共前后缀的长度为j,标记j的位置,以用来之后将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){
printf("%d ", i - n);
j = ne[j]; //j指针回退,继续寻找下一个能匹配的位置
}
} return 0;
}