KMP学习笔记

zsy说过:”一切字符串算法的本质都是有效利用失配信息进行匹配或查询!“

一、 KMP是什么

给出两个字符串 \(s1\)\(s2\),若 \(s1\) 的区间 \([l,r]\) 子串与 \(s2\) 完全相同,则称 \(s2\)\(s1\) 中出现了,其出现位置为 \(l\)
现在请你求出 \(s2\)\(s1\) 中所有出现的位置。

(我们把 \(s2\) 称作模式串,把 \(s1\) 称为文本串, \(s1\) 可能非常长)

二、 暴力

显然,我们可以两层循环,枚举起始位置,然后一一比较,如果失配就 continue 到下一个位置。它的时间复杂度是 \(O(nm)\)

for i:=1 to n do
begin
	for j:=1 to m do
	begin
		 if a[i+j-1]<>b[j] then break;
		 if j=m writeln(i);
	end;
end; 

三、 优化暴力

考虑这个暴力慢在什么地方:每一次失配后, \(i\)\(j\) 两个指针都要回跳。如果能保证 \(i\) 指针单调不减,只移动 \(j\) 指针,就会快很多——时间复杂度变成了 \(O(n+m)\)

这当然是可行的,观察下面这组样例:

A B A C
A B A B A C A

在第4位失配后,如果只移动模式串,可以直接把模式串的第2位移动到与文本串的第4位对齐,如下:

    A B A C
A B A B A C A

这是用肉眼看出来的结果,怎么让程序知道,在第 j 位失配后,应该跳到第几位?能否预处理出一个移动数组?需要满足什么性质?


为保证正确性,这个 \(nxt\) 数组有如下三条性质

  1. nxt[1]=0 ;

  2. s2[nxt[j]]=s2[j] ;

  3. s2[1~nxt[j]]s2[j-nxt[j]+1]~j] 一一相等.

如果得到了这个数组,在 \(i\)\(j+1\) 失配时,就只需使 j=nxt[j]\(i\) 不需改变。

四、 代码实现

假装已经知道了 \(nxt\) 数组,用下面这段代码就能轻松求出 \(s2\)\(s1\) 的位置。

j:=0;
for i:=1 to length(a) do
begin
	while(j>0) and (a[i]<>b[j+1]) do
            j:=nxt[j];
	if a[i]=b[j+1] then inc(j);
	if j=length(b) then
	begin
		writeln(i-length(b)+1);
		j:=nxt[j];
    end;
end;

怎么求出 \(nxt\) 数组呢?用模式串跟自己匹配即可,只需把最后一个 if 语句改为 nxt[i]:=j 。代码如下:

nxt[1]:=0;
for i:=2 to length(b) do
begin
	while(j>0) and (b[i]<>b[j+1]) do 
           j:=nxt[j];
	if b[i]=b[j+1] then inc(j);
	nxt[i]:=j;
end;

这样做是正确的,因为跑到 nxt[i]:=j 这句话,说明前面一定已经匹配好了,符合前文的两条性质。

求这个竟然也有模板:P4391 [BOI2009]Radio Transmission 无线传输

五、 AC 代码

P3375 【模板】KMP字符串匹配

var
	a,b:ansistring;
	i,j:longint;
	nxt:array[0..1000010] of longint;
begin
	readln(a);
	readln(b);
	nxt[1]:=0;
	for i:=2 to length(b) do
	begin
		while(j>0) and (b[i]<>b[j+1]) do 
            j:=nxt[j];
		if b[i]=b[j+1] then inc(j);
		nxt[i]:=j
	end;
	j:=0;
	for i:=1 to length(a) do
	begin
		while(j>0) and (a[i]<>b[j+1]) do
            j:=nxt[j];
		if a[i]=b[j+1] then inc(j);
		if j=length(b) then
		begin
			writeln(i-length(b)+1);
			j:=nxt[j];
        end;
	end;
	for i:=1 to length(b) do
		write(nxt[i],' ');
    writeln;
end.

THE END

posted @ 2021-08-13 20:31  q0000000  阅读(36)  评论(0编辑  收藏  举报