声明
本章仅是一个模板。
给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
题目内容
模板串 P 在模式串 S 中多次作为子串出现。
求出模板串 P 在模式串 S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。
数据范围
1≤N≤105
1≤M≤106
输入样例:
3
aba
5
ababa
输出样例:
0 2
朴素版本(暴力版)
思想:
#include<iostream>
using namespace std;
const int N = 10010, M = 100010;
char s[M], p[N];//模板串 P 在模式串 S 中多次作为子串出现。
int n, m;
int main()
{
cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
cin >> n >> p + 1;//子串
cin >> m >> s + 1;//母串
for (int i = 1,j=1; i <= m; i++) //枚举母串的每一个结点
{
bool flag = true;
for (j = 1; j <= n; j++) //枚举子串
{
if (s[i+j-1]!=p[j])
{
flag = false;
break;
}
}
if (flag == true)
cout << i-1 << endl;
}
return 0;
}
KMP算法
思路: 在朴素的做法的基础上进一步优化,使子串具有回溯功能。
具体代码实现
#include<iostream>
using namespace std;
const int N=100010,M=1000010;
char q[N],s[M];
int ne[N];//保存next数组
int main()
{
int n,m;
cin>>n>>q+1>>m>>s+1;//下标均从1开始
for(int i=2,j=0;i<=n;i++)
//j表示匹配成功的长度,i表示q数组中的下标,因为q数组的下标是从1开始的,只有1个时,一定为0,所以i从2开始
{
while(j&&q[i]!=q[j+1]) j=ne[j];
//如果不行可以换到next数组
if(q[i]==q[j+1]) j++;
//成功了就加1
ne[i]=j;
//对应其下标
}
//j表示匹配成功的长度,因为刚开始还未开始匹配,所以长度为0
for(int i=1,j=0;i<=m;i++)
{
while(j&&s[i]!=q[j+1]) j=ne[j];
//如果匹配不成功,则换到j对应的next数组中的值
if(s[i]==q[j+1]) j++;
//匹配成功了,那么j就加1,继续后面的匹配
if(j==n)//如果长度等于n了,说明已经完全匹配上去了
{
printf("%d ",i-j);
//因为题目中的下标从0开始,所以i-j不用+1;
j=ne[j];
//为了观察其后续是否还能跟S数组后面的数配对成功
}
}
return 0;
}