KMP 自动机

KMP 自动机

有时候我们需要重复跳KMP上的nxt[],每次都跳太劣了,我们考虑优化它。

显然,我们都知道,每个nxt[i],都有nxt[i]<i,我们将所有nxt[i]->i连边,我们会发现它们构成一棵树,这被叫做失配树

我们每次跳nxt[]的过程都相当于在树上跳祖先,我们发现很多过程在树上都变得显然了。

比如我们要在另一个字符串上匹配一个串,我们找到最长的匹配长度,就相当于跳祖先跳到另一个串

我们发现这个转移构成一张图,在图上找点就做完了。

我们发现跳祖先太慢了,多次操作可能会T

例题 Prefix Function Queries

题意简述:每次在\(S\)串后加几个字符,求新加进来的字符的nxt[],询问间独立。

数据范围\(|S|\le 10^6,q\le 10^5,|T|\le 10\)

我们暴力跳,时间复杂度是\(O(|S|+|S||T|q)\),显然不能过。

我们发现跳nxt[]是个非常单调的过程,我们每次都在往前跳直到\(s[j+1]=s[i]\)

我们发现这个东西的数量很小,只有\(O(1e6\times 26)\),不妨将它离线下来:

我们定义\(T(i,j)\)\(i\)开始,往前跳nxt[],直到\(nxt[i]=j\)为止,我们最后在的位置。

本质上就是\(i\)匹配字符\(j\)的位置。

有:

\[T(i,j)=T(nxt[i],j) \]

我们要将前\(n\)个和后\(m\)个接起来。

所以我们要强制让第\(n+1\)\(n\)接起来,让它从\(n\)开始跳。

所以有:

\[T(n,s[n+1])=n \]

然后这道题就做完了,但是我们对算法的探索永无止境

前面我们提到所有的nxt[]构成一颗失配树,我们将每个点拆成\(O(26)\)个状态。

我们发现,在原有的失配树上,有一些其他的边,从一个点指向它的祖先中的一点,容易发现,我们打破的原来的树性质,加入了一些压缩后的路径,它构成一个DAG

我们在压缩后的路径上跳是\(O(1)\)的,这就是KMP 自动机

posted @ 2024-10-04 18:28  lichenyu_ac  阅读(106)  评论(0)    收藏  举报