题目描述
如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。
为了减少骗分的情况,接下来还要输出子串的前缀数组next。
(如果你不知道这是什么意思也不要问,去百度搜[kmp算法]学习一下就知道了。)
输入输出格式
输入格式:第一行为一个字符串,即为s1(仅包含大写字母)
第二行为一个字符串,即为s2(仅包含大写字母)
输出格式:若干行,每行包含一个整数,表示s2在s1中出现的位置
接下来1行,包括length(s2)个整数,表示前缀数组next[i]的值。
输入输出样例
说明
时空限制:1000ms,128M
数据规模:
设s1长度为N,s2长度为M
对于30%的数据:N<=15,M<=5
对于70%的数据:N<=10000,M<=100
对于100%的数据:N<=1000000,M<=1000000
样例说明:
所以两个匹配位置为1和3,输出1、3
复习了下kmp
这题和裸kmp唯一的区别是当匹配成功之后,还要再沿着失配边回去,继续与之后的字符匹配。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000000;
typedef long long LL;
typedef double DB;
inline int get(){
char c;
while((c = getchar()) < '0' || c > '9');
int cnt = c - '0';
while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';
return cnt;
}
char s1[MAXN + 10],s2[MAXN + 10];
int next[MAXN + 10];
int main(){
#ifdef lwy
freopen("1.txt","r",stdin);
/* #else
freopen(".in","r",stdin);
freopen(".out","w",stdout);*/
#endif
scanf("%s",s1); scanf("%s",s2);
int len1 = strlen(s1);
int len2 = strlen(s2);
next[0] = next[1] = 0;
for(int i = 1; i < len2; i ++){
int j = next[i];
while(j && s2[i] != s2[j]) j = next[j];
if(s2[i] == s2[j]) next[i + 1] = j + 1;
else next[i + 1] = 0; // i -> i + 1
}
int k = 0;
for(int i = 0; i < len1; i ++){
while(k && s1[i] != s2[k]) k = next[k];
if(s1[i] == s2[k]) k ++;
if(k == len2){
printf("%d\n",i - k + 2);
k = next[k];
}
}
for(int i = 1; i <= len2; i ++){
printf("%d ",next[i]);
}
return 0;
}