拓展kmp&Exkmp

来源:互联网 发布:诺贝尔经济学奖知乎 编辑:程序博客网 时间:2024/05/29 18:00

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;}
原创粉丝点击