扩展kmp小结

定义

在线性的时间复杂度内求出每一个s[i..n]与t的最长公共前缀

算法思路

定义next[i]为满足s[i..i+l]=t[0..l]情况下最长的l的长度
ex[i]为满足t[i..i+l]=t[0..l]情况下的最长的长度
首先考虑一下朴素的算法,发现复杂度是nm的,但是中间似乎和kmp一样有很多重复的比较
那么我们怎么样尽量去减少重复的比较呢?
这里写图片描述
在朴素的算法中,我们发现第一次求可以求出s[0..3]=t[0..3],那么就有s[1..3]=t[1..3]
然后再这个字符串中next[1]=4,那么根据next的定义,得:t[1..4]=t[0..3],那么t[0..2]=t[1..3]
整理几条式子,得:1:s[1..3]=t[1..3] 2:t[1..3]=t[0..2]
所以:s[1..3]=t[0..2]
这样我们不用比较就可以得到ex[1]的最小值了~
然后再在这个基础上往后配对
同时这也是exkmp的大概思想

扩展kmp大概的流程:
首先假设我们现在已经知道ex[0]到ex[i]的值现在我们想求ex[i+1]的值
定义mx为0到i中i+ex[i]最大的一个,id为获得最大的mx对应的i值
然后就有两种情况
1: i+next[iid]<mx
分析一下此时的情况
设len=next[i-id]
那么有:1:s[i..i+len-1]=s[i-id..i-id+len-1] 2:s[i-id..i-id+len-1]=t[0..len-1]
那么在 len1+i<=mx 时可以直接匹配len位,换言之,有: next[iid]+i<mx
所以有:next[i-id]+i< mx时,有:ex[i]=next[i-id]
2: i+next[iid]>mx
这个时候最末尾的位置已经大于mx了,但是我们最远只匹配到mx的位置,并不能确定后面的位置和前面的任何一个位置相等,所以我们要继续在t[mx-i]与s[mx]位置开始继续往下找
最后更新id和mx
可以发现每一次比较都会使mx往后移一位,所以时间复杂度是O(N)的
值得注意的是在i+next[i-id]=mx时后面也可能是匹配的,所以算入情况2
另一个值得注意的地方是i可能大于mx,这时从第0位开始找
而next数组的求法是完全一样的,不再赘述
一开始的初始化看代码就好了

贴代码

var
    next,ex:array[0..200005]of longint;
    a,b:array[0..200005]of char;
    i,j,k,l,m,n,mx,id,ne,p:longint;
    ans:int64;
begin
    //assign(input,'EX_KMP.in'); reset(input);
    n:=0;
    while not eoln do
    begin
        read(a[n]);
        inc(n);
    end;
    readln;
    m:=0;
    while not eoln do
    begin
        read(b[m]);
        inc(m);
    end;
    readln;
    b:=a;
    m:=n;
    dec(n);
    dec(m);
    next[0]:=n+1;
    for i:=1 to n do
    if a[i]=a[i-1] then inc(next[1]) else break;
    id:=1;
    mx:=next[1];
    for i:=2 to n do
    begin
        ne:=i+next[i-id];
        if ne<mx then next[i]:=next[i-id] else
        begin
            p:=mx-i;
            if p<0 then p:=0;
            while (a[p]=a[i+p]) and (i+p<=n) do inc(p);
            next[i]:=p;
            id:=i;
            mx:=i+p;
        end;
    end;
    for i:=0 to n do if a[i]=b[i] then inc(ex[0]) else break;
    id:=0;
    mx:=ex[0];
    for i:=1 to n do
    begin
        ne:=i+next[i-id];
        if ne<mx then ex[i]:=next[i-id] else
        begin
            p:=mx-i;
            if p<0 then p:=0;
            while (a[p]=b[i+p]) and (p<=n) and (i+p<=m) do inc(p);
            ex[i]:=p;
            id:=i;
            mx:=i+p;
        end;
    end;
    for i:=0 to n do inc(ans,ex[i]);
    writeln(ans);
   // close(input);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值