拓展kmp&Exkmp

9 篇文章 0 订阅
5 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值