给出两个字符串 s1 和 s2,若 s1 的区间 [l,r] 子串与 s2 完全相同,则称 s2 在s1 中出现了,其出现位置为 l。
现在请你求出 s2 在 s1 中所有出现的位置。
定义一个字符串 s 的 border 为 s 的一个非 s 本身的子串 t,满足 t 既是 s 的前缀,又是 s 的后缀。
对于 s2,你还需要求出对于其每个前缀 ′s′ 的最长 border ′t′ 的长度。
输入格式
第一行为一个字符串,即为 s1。
第二行为一个字符串,即为 s2。
输出格式
首先输出若干行,每行一个整数,按从小到大的顺序输出 s2 在 s1 中出现的位置。
最后一行输出 ∣s2∣ 个整数,第 i 个整数表示 s2 的长度为 i 的前缀的最长 border 长度。
输入输出样例
输入
ABABABC ABA
输出
1 3 0 0 1 KMP 的精髓在于,对于每次失配之后,都不会从头重新开始枚举,而是根据已知的数据,从某个特定的位置(与后缀对应的前缀)开始匹配;而对于模式串的每一位,都有唯一的前缀,这个在失配之后的位置可以帮助我们利用已有的数据不用从头匹配,从而节约时间。
如abcdabaadc
abaac
当c与d不对应时将首端的a与d前的a进行对应而不是从头匹配
#include<iostream> #include<cstring> #define MAXN 1000010//定义MAXN方便后续更改 using namespace std; int kmp[MAXN]; int la,lb,j; char a[MAXN],b[MAXN]; int main() { cin>>a+1;//从a[1]开始输入便于计算 cin>>b+1; la=strlen(a+1); lb=strlen(b+1); for (int i=2;i<=lb;i++) { while(j&&b[i]!=b[j+1]) j=kmp[j]; if(b[j+1]==b[i]) { j++; kmp[i]=j; } }//对于lb上每个字符做上标记,前后缀字符具有关系 j=0; for(int i=1;i<=la;i++) { while(j>0&&b[j+1]!=a[i])//如果lb与la不相等,则将j调整到前缀位置,诺不相等则调整到首位 j=kmp[j]; if (b[j+1]==a[i]) j++; if (j==lb) { cout<<i-lb+1<<endl;//i-lb+1为题目所求L j=kmp[j];//将j调整到字符串末尾左侧第一个相等处 } } for (int i=1;i<=lb;i++) cout<<kmp[i]<<" "; return 0; }