这个算法通常用来解决B是否是A的子串,也比较有侷限性。
看了一晚上解释我终于明白了。
很短,很好写,很好理解。
定义一些东西
A:一个串
B:另一个串
j:a[i-j+1…..i]=b[1…j] 随着i的变化而变化,但与i的取值无关,用更通俗的话来讲,就是已经匹配了多少位。
P[i]:b[1…p[i]]=b[j-p[i]+1…..j],有没有发现与上面非常相似,就是前p[i]位与后p[i]位相等。
先列个数据(实在找不到好的数据,就copy了别人的一个数据)
A:a b a b a b a a b a b
B:a b a b a c b
这时对于B来说,P数组的值为[0,0,1,2,3,0,0]
然后我们惊奇地发现,在第6个位置炸了,无法匹配,所以要找新的,怎么找呢,我们有P数组,所以用P[5],p[5]=3,取后三位,可以转换后接着找。
A:a b a b a b a a b a b
B:…..a b a b a c b
这时我们惊奇地发现,在第六个位置是可以的
但这时候在第7个位置还是不行,再转换,因为j=4,所以p[4]=2
A:a b a b a b a a b a b
B:………a b a b a c b
尽管我们很努力,但事实证明这不能匹配,不过从中我们可以观察到kmp的匹配过程——
逐位匹配,不行就换。很清晰,很明了。
for i:=1 to length(a)do
begin
while(j>0)and(a[i]<>b[j+1])do j:=p[j];
if(a[i]=b[j+1])then inc(j);
if(j=length(b))then
begin
writeln('YES');
halt;
end;
end;
writeln('NO')
我们遗留了一个问题,就是P数组怎么求
显然这个P数组求的过程与我们正式匹配的过程非常相似,对吧。
机智的你应该会发现,P就是b串自我匹配
for i:=2 to length(b) do
begin
while(j>0)and(b[i]<>b[j+1])do j:=p[j];
if(b[i]=b[j+1])then inc(j);
p[i]:=j;
end;
想象一下,写写数据,脑补一下过程。
其实我一开始也不明白的,后来拿着程序跑一个数据,一步一步跑,就慢慢理解了。
学算法,不一定要快,就算比别人慢,只要学会了,都是一样的。
可能有人会有疑问
为什么P可以用来转换位置后接着匹配?
假设当前 a[i-j+1…..i]=b[1…j]
P数组它表示的前p[j]位与后p[j]位相等,也就是说a[i-j+1…i]这个区间的前p[j]与后p[j]位也相等,
感性理解一下,就是转换成a的后p[i]位与b的前p[i]位匹配,对吧?所以这就很明显了。