题目描述
给定一个字符串 S,以及一个模式串,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 P在字符串 S中多次作为子串出现。
求出模式串 P在字符串 S中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0开始计数),整数之间用空格隔开。
数据范围
1≤N≤105
1≤M≤106
样例
输入样例:
3
aba
5
ababa
输出样例:
0 2
KMP算法
关键代码
//前缀函数,记录子串s[i]中最长相等的真前缀和真后缀的长度
void previx_arr(){
ne.push_back(0);
for(int i = 1;i < s.size(); i ){
//相邻的前缀函数值至多增加1,当移动到下一个位置时,
//前缀函数的值要么增加一,要么维持不变,要么减少。
//且前缀函数的值刚好对应下一个需要匹配的位置的下标
//所以进行下一次匹配时,将j初始化为ne[i-1]
int j = ne[i - 1];
//若不匹配,j继续回跳至次长的前缀函数值
while(j > 0 && s[i] != s[j]) j = ne[j - 1];
//若匹配成功,则当前最长匹配前缀长度
if(s[i] == s[j]) j ++;
ne.push_back(j);
}
}
C++ 代码
```#include[HTML_REMOVED]
using namespace std;
string s, t;
vector[HTML_REMOVED] ne;
//前缀函数,记录子串s[i]中最长相等的真前缀和真后缀的长度
void previx_arr(){
ne.push_back(0);
for(int i = 1;i < s.size(); i ){
//相邻的前缀函数值至多增加1,当移动到下一个位置时,
//前缀函数的值要么增加一,要么维持不变,要么减少。
//且前缀函数的值刚好对应下一个需要匹配的位置的下标
//所以进行下一次匹配时,将j初始化为ne[i-1]
int j = ne[i - 1];
//若不匹配,j继续回跳至次长的前缀函数值
while(j > 0 && s[i] != s[j]) j = ne[j - 1];
//若匹配成功,则当前最长匹配前缀长度
if(s[i] == s[j]) j ++;
ne.push_back(j);
}
}
int main(){
int n, m;
cin >> n >> s >> m >> t;
previx_arr();
for(int i = 0, j = 0; i < m; i ){
//当文本串当前字符与模式串当前字符不匹配时,进行回退
// 根据前缀函数值回退到次长的真前缀位置,继续匹配
while(j > 0 && t[i] != s[j]) j = ne[j - 1];
// 如果匹配成功,将模式串匹配位置右移一个字符
if(t[i] == s[j]) j ;
//当模式串完全匹配时,输出当前匹配的起始位置,并将j指向下一该匹配的位置
if(j == n){
cout << i - n + 1 << ” “;
j = ne[j - 1];
}
}
return 0;
}
```