title: Introduction to KMP tags:
在字符串理论中,经常要求一个字符串$T$在另一个字符串$S$中所有出现的位置。这个过程就是字符串匹配。我们称$S$为文本,$T$为模式。下面介绍一种时间复杂度为线性的算法 KMP,可以解决此类问题。
令$Si$表示字符串$S$的第$i$个字符,$S{i, j}$表示字符串$S$的第$i$到第$j$个字符构成的字符串。为了方便讲解,字符串下标从$1$开始。
在 KMP 中有一个重要的数组,我们称它为$nxt$。对于一个字符串$S$,$nxti=\max(x \mid x \lt i \land S{1, x}=S_{i-x+1, i})$。特别的,令$nxt_1=0$。
考虑如何求出模式$T$的$nxt$数组。最暴力的暴力时间复杂度为$O(n^2)$,但这实际上浪费了很多有用的信息。其实可以从$nxt_{i-1}$推到$nxt_i$。
我们来形象化这个过程
v
acbacabacbacb
^
假设我们已经求出$nxt_{12}=5, \ nxt5=2$,当前两个指针分别指向$S{12}$和$S{nxt{12}}=S5$,求$nxt{13}$。在这里,两个指向$S_p, Sq$的指针表示$S{p-q+1, p}=S_{1, q}$。
比较两个指针的后一位。易知,若$S_{13}=S6$,则$nxt{13}=nxt_{12}+1=6$,可以将两个指针都向后移一位。
否则,我们已经知道$nxt{12}=5$,即$S{1, 5}=S_{8, 12}$,而$nxt5=2$,即$S{1, 2}=S{4, 5}=S{11, 12}$。这意味着我们可以把指向$S{nxt{12}}$的指针重新指向$S{nxt{nxt_{12}}}=S_2$,如下
v
acbacbaacbacb
^
此时仍然满足$S{12-2+1, 12}=S{1, 2}$。再次比较两个指针的后一位,发现$S_{13}=S3$,于是$nxt{13}=nxt{2}+1=3$,将两个指针向后移一位。当然,若$S{13} \neq S3$则需要将指向$S{nxt5}$的指针再指向$S{nxt_{nxt_5}}$,重复上述过程,直到指针指向$S0$,那么$nxt{13}=0$。
代码并不复杂(该代码中的字符串下标从$0$开始)
void kmp(char c[], int len) {
nxt[0] = -1;
for (int i = 1; i < len; ++i) {
for (nxt[i] = nxt[i - 1]; ~nxt[i] && c[nxt[i] + 1] != c[i];
nxt[i] = nxt[nxt[i]])
;
if (c[nxt[i] + 1] == c[i])
++nxt[i];
}
}
在得到模式$T$的$nxt$数组后,我们就可以用类似的方法求出$T$在文本$S$中所有出现的位置了。思路也是维护两个指针,一个在$S$上,指向$S_p$,另一个在$T$上,指向$Tq$,同样要满足$S{p-q+1, p}=T_{1, q}$。显然,当$q=|T|$时,就找到了一个出现的位置。
KMP 算法除了可以用于字符串匹配,还能求出一个循环串的最小循环节。循环串是由至少两个相同的字符串拼接起来得到的字符串。
具体来讲,设$i=|S|$,$nxt_i \gt 0 \land i-nxti \mid i \Rightarrow S{1, i}$是循环串,它的最小循环节是$S_{1, i-nxt_i}$。
要证明以上结论,只需要证明:
证明 显然,在满足上述条件的情况下,$S$的确是循环串,$S_{1, i-nxt_i}$就是$S$的一个循环节。
证明 设$|A| \le |B|$,由$AB=BA$可知,$A=B{1, |A|}=B{|A|+1, 2|A|}=\cdots$,那么当$(|A|) \mid (|B|)$时,$A$是$B$的一个循环节(或$A=B$),因此$AB$是循环串。
当$(|A|) \nmid (|B|)$时,令$k=\left\lfloor {|B| \over |A|} \right\rfloor$。
考虑字符串$S=AB, \ T=S{(k-1)|A|+1, |S|}$,可知$T{1, |A|}=T{|T|-|A|+1, |T|}=A, \ T{1, |T|-|A|}=T{|A|+1, |T|}=B{(k-1)|A|+1, |B|}$。令$A'=B_{(k-1)|A|+1, |B|}, \ B'=A$,那么我们又得到了$A'B'=B'A'$的形式,此时$|A'|=|B| \bmod |A|, \ |B'|=|A|$。因此,最后一定能得到长为$(|A|, |B|)$的字符串,它是$A, B$的一个循环节,也是$AB$的一个循环节。
证明 令$x$为最小循环节的长度,只要证明不存在$y$满足$y \lt x$且$S{1, i-y}=S{y+1, i}$。
假设存在这样的$y$,那么$S{y+1, y+x}=S{1, x}=S{x+1, 2x}$。令$A=S{y+1, x}, \ B=S{x+1, y+x}$,可以发现$A$是$S{1, x}$的后缀、$S{y+1, y+x}$的前缀,$B$是$S{x+1, 2x}$的前缀、$S{y+1, y+x}$的后缀,即$S{1, x}=AB=BA$,根据引理 2,$S{1, x}$是循环串,这与$x$是最小循环节的长度矛盾。因此这样的$y$不存在,而因为$S{1, x}$是一个循环节,所以$S{1, i-x}=S{x+1, i}$,根据 KMP 的原理,$nxti=i-x$,即$S$的最小循环节是$S{1, i-nxt_i}$。
由此命题得证。