caioj1178
【题目描述】
给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](1<=i<=lenA),求出A[i..lenA]与B的最长公共前缀长度
【输入格式】
输入A,B两个串,(lenB<=lenA<=1000000)
【输出格式】
输出lenA个数,表示A[i…lenA]与B的最长公共前缀长度,每个数之前有空格
【样例输入】
aabbabaaab
aabb
【样例输出】
4 1 0 0 1 0 2 3 1 0
这就是exkmp的模版题了。。
类似kmp,定义两个数组
int extend[1110000];//extend[i]表示s1[i]与s2[1]开头的最长公共前缀
int pre[1110000];//pre[i]表示s2[i]与s2[1]开头的最长公共前缀
一点点不同的地方就是这里维护的是前缀而不是像kmp那样维护前缀后缀
那到底怎么做呢?
先设一个a,表示a+pre[a]-1最大的,即a往后推pre[a]-1这么多最大
那么可以肯定,对于a~a+pre[a]-1这个矩形,前面一定有一个与之相同的矩形1~1+pre[a]-1。如下图
设当前访问到k,可以得知k一定大于a。但是k不一定小于a+pre[a]-1。当k大于的情况就暴力推就好啦
剩下就是k在a~a+pre[a]-1这个矩形里面了
首先可以看出,对于每一个k,在前一个矩形内一定有一个和他完全相同的k-a
那么,由于我们k是从小到大开始枚举的,k-a的结果一定计算完毕
分成三种情况
pre[k-a]小于pre[a] 即k-a到(k-a)+pre[k-a]-1这个矩形右边框小于第一个矩形
pre[k-a]大于pre[a] 即k-a到(k-a)+pre[k-a]-1这个矩形右边框大于第一个矩形
pre[k-a]等于pre[a] 即k-a到(k-a)+pre[k-a]-1这个矩形右边框等于第一个矩形
第一种,等于
由于左右两个矩形完全相同,那么k-a延伸的矩形一定在左边的矩形之内,所以k延伸的矩形也一定在右边的矩形之内,直接赋值
第二种,大于
由于左右两个矩形完全相同且左右矩形右边框分别加1后所在位置的字符不同,且k-a延伸的矩形右边框不在左边矩形之内,所以k延伸的矩形长度即为k-a延伸的矩形在左边矩形内的长度
最后一种,等于
由于k-a延伸的矩形右边框等于左边矩形右边框,我们只能知道k延伸的矩形一定有k-a延伸的矩形这么长。但是由于左边矩形右边框+1 不等于右边矩形右边框+1.所以说这里只能暴力开始推了
做完之后pre数组就出来啦
然后根据pre数组和exkmp的定理,将母串与子串做一次类似exkmp,就可以得出extend数组啦
由于太懒pre被我省略成p了
模板code——caioj1178
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
char s1[1110000],s2[1110000];
int extend[1110000];//extend[i]表示s1[i]与s2[1]开头的最长公共前缀
int p[1110000];//p[i]表示s2[i]与s2[1]开头的最长公共前缀
int lenx,leny;
void exkmp()
{
int x,k;
p[1]=leny;//第一个与第一个的最长公共前缀即为leny
x=1;
while(s2[x]==s2[x+1])x++;
p[2]=x-1;k=2;
for(int i=3;i<=leny;i++)
{
int pp=k+p[k]-1;//边界
int L=p[i-k+1];
if(i+L-1<pp)p[i]=L;
else//最长前缀大于等于边界或者i>k
{
int j=pp-i+1;
if(j<0)j=0;
while(s2[j+1]==s2[j+i] && i+j<=leny)j++;
p[i]=j;
k=i;//记录
}
}
x=1;
while(s1[x]==s2[x] && x<=leny)x++;
extend[1]=x-1;k=1;
for(int i=2;i<=lenx;i++)
{
int pp=k+extend[k]-1;
int L=p[i-k+1];
if(i+L-1<pp)extend[i]=L;
else
{
int j=pp-i+1;
if(j<0)j=0;
while(s1[j+i]==s2[j+1] && i+j<=lenx && j<=leny)j++;
extend[i]=j;
k=i;
}
}
}
int main()
{
scanf("%s",s1+1);
scanf("%s",s2+1);
lenx=strlen(s1+1);leny=strlen(s2+1);
exkmp();
for(int i=1;i<lenx;i++)printf("%d ",extend[i]);
printf("%d\n",extend[lenx]);
return 0;
}