KMP字符串题解
题目描述
给定一个模式串 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
时间复杂度
O=(2m)
#include <iostream>
using namespace std;
const int N=2e6+5;
int ne[N];
char p[N],s[N];
/*
ne数组存一下以每个位置结尾的“可匹配的最长前后缀的长度 ”
例p[]={" aba"};这里从p[1]开始存储,个人习惯
ne[1] p[1]=a 因为需要滑动,那字符串就不能和自身相比
所以只有一个字符的时候,无法移动 ne[1]为0
ne[2] p[1,2]=ab,前缀最多可能为a,后缀为b
a!=b,所以也无法移动 ne[2]=0;
ne[3] p[1,2,3]=aba 前缀可能为a,ab 后缀可能为a,ba;
a==a,ab!=ba,所以最长匹配前后缀长度为 1 ne[3]=1
*/
int main(){
int n,m;
cin>>n>>p+1;
cin>>m>>s+1;//输入模式串,从下标1开始存储
//构建ne数组,ne的意思就是next,但next比较特殊,防止报错,就用ne代替
for(int i=2,j=0;i<=n;i++){
while(j&&p[i]!=p[j+1]){//对模式串进行匹配
j=ne[j];//如果没匹配成功,j就变成ne[j]的位置
}
//跳出循环一共有两种情况,j==0,一直匹配失败,ne数组从0开始重新计算
// p[i]==p[j+1] 匹配成功 ne数组进入下一位计算
if(p[i]==p[j+1])
j++;//
ne[i]=j;//匹配成功的i位与j位的对应关系
}
//看看我们的ne数组,此步多余,就是单纯的看看ne数组
// for(int i=0;i<=n;i++){
// cout<<ne[i]<<' ';
// }
cout<<endl;
//利用ne数组进行,模式匹配
for(int i=1,j=0;i<=m;i++){
while(j&&s[i]!=p[j+1]){//对模式串进行匹配
j=ne[j];//匹配失败j到ne[j]的位置
}
if(s[i]==p[j+1])
j++;//匹配成功进入下一位
if(j==n){//完全匹配成功
cout<<i-n<<' ';//输出s数组中匹配成功字符串的首位置,即当前位置-模式串长度
j=ne[j];//寻找p在s中的下一次出现的位置,利用ne数组优化时间复杂度
}
}
/*
最后看看时间复杂度
匹配两个字符串
1、从i=1的位置开始逐个匹配,利用ne数组减少比较次数
2、i与j+1的位置不匹配(假设已知1~j匹配i-j~i-1)
3、j调回ne[j]继续比较(因为 1~j匹配i-j~i-1所以1~ne[j]也能匹配到i-ne[j]~i-1)
4、成功后依然j=ne[j],就是把这次成功当做失败,继续匹配下一个位置
因为j最多加m次 在加之前j每次都会减少且最少减一,j>0
所以while循环最多执行m次,如果大于m次,j会小于0
所以O(2m)
*/
/*
给个样例大家试试
输入
3
aba ->p数组
5
ababa ->s数组
输出
0 2
*/
return 0;
}