扩展KMP问题
给定母串
s
,和子串
引例
s=′aaaaabaa′
t=′aaaaaa′
首先通过六次运算,我们得到了
extend1=5
,接下来我们要计算
extend2
,那是不是也要计算五次?在计算
extend1
之后我们得到这样的信息
s[2..5]=t[2..5]
,在计算
extend2
的时候其实是
s[2..n]
在匹配t,又因为
s[2..5]=t[2..5]
,那么在匹配的开头阶段就是以
t[2..5]
为母串,t为子串的匹配。
next数组的引入
定义
nexti
表示
t[i..m]与t的最长公共前缀长度
对上述例子,
next2=5
,可以得到
t[1..5]=t[2..6],可推出t[1..4]=t[2..5]
,
又因为
s[2..5]=t[2..5],所以s[2..5]=t[1..4],
也就是说前四位的匹配是完全可以避免的。
进入正题
提出更加一般的算法,假设此时1到k中的
extendi
(黄色部分)已经算法算好,设
p=max{i+extendi−1}1≤i≤k,a是取这个最大值的i
,
如下图:
我们可以得到
s[a..p]=t[1..p−a+1]从而推出s[k+1..p]=t[k−a+2..p−a+1],设l=nextk−a+2
此时会有两种情况
上面的红色部分是相等的,
(因为
s[k+1..p]=t[k−a+2..p−a+1],而l又等于nextk−a+2
),但蓝色部分肯定不相等
(如果相等,那么
t[1..l+1]=s[k+1..k+l]=t[k−a+2..p−a]
,那么此时
nexti=next1+1
,矛盾),
结论:所以我们无需比较,即可知道
nextk+1=l
紫色的位置以及以后的位置都是未被探知的,所以此时就要从
s[p+1]和t[p−k+1]这里开始匹配,直到失配为止,并同时将a更新为k+1。
next数组的求法与extend的求法类似,具体看代码。
代码:
var
next,extend:array[0..1000000]of longint;
s,t:ansistring;
i,j,a,l,lx,p:longint;
function max(i,j:longint):longint;
begin
if i<j then max:=j else max:=i;
end;
begin
readln(t);
readln(s);
j:=0;
while (2+j<=length(t))and(t[1+j]=t[2+j]) do inc(j);
next[1]:=length(t);
next[2]:=j;
a:=2;
for i:=3 to length(t) do
begin
p:=a+next[a]-1;
l:=next[i-a+1];
if l<p-i+1 then//自己**仔细**体会(对比上文)
next[i]:=l
else
begin
j:=max(0,p-i+1);
while (i+j<=length(t))and(t[i+j]=t[1+j]) do inc(j);
next[i]:=j;
a:=i;
end;
end;
j:=0;
while (j<length(S))and(j<length(t))and(t[1+j]=S[1+j]) do inc(j);
extend[1]:=j;
a:=1;
for i:=2 to length(s) do
begin
p:=a+extend[a]-1;
l:=next[i-a+1];
if l+i<p+1 then
extend[i]:=l
else
begin
j:=max(0,p-i+1);
while(i+j<=length(S))and(j<length(t))and(S[i+j]=t[1+j]) do inc(j);
extend[i]:=j;
a:=i;
end;
end;
end.
the end
由于我的水平有限,难免会有些写错的地方,希望大家批评指正,多多包容,thank you for your patience.