定义
在线性的时间复杂度内求出每一个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[i−id]<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]
那么在
len−1+i<=mx
时可以直接匹配len位,换言之,有:
next[i−id]+i<mx
所以有:next[i-id]+i< mx时,有:ex[i]=next[i-id]
2:
i+next[i−id]>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.