edu134 E(KMP自动机,KMP复杂度分析)
E. Prefix Function Queries
Q:给定一个仅由小写字母构成,长度为 \(n\) 字符串 \(s\)。\(q\) 次查询,每次查询给定一个长度为 \(m\) 的字符串 \(t\),求将 \(t\) 接在 \(s\) 后面时,新部分的每个位置的 \(next\) 值。
\(n \le 10^{6}, q \le 10^{5}, m \le 10\)
一个很显然的想法,就是将 \(s\) 每个位置的 \(next\) 值预处理出来,然后继续依次处理新部分的每个位置的 \(next\) 值。虽然总共只有 \(O(q*m)\) 个字符,依据 KMP 算法的线性性质会推测出总复杂度也是 \(O(q*m)\),但需要注意的是,KMP 的线性时间复杂度是均摊得到的,也就是说执行 KMP 算法过程中,只看某个局部的复杂度,最坏情况下也会达到 \(O(n)\),并不一定与其长度成正比。因此这样做实质上是暴力,最坏复杂度可达到 \(O(nq)\)。
问题的关键在于每次求新位置的 \(next\) 值时,仍然需要按照传统 KMP 求 \(next\) 数组的方式,一步步暴力往前跳。有没有什么办法能够避免这一过程,快速找到最终需要到达的位置呢?
很容易联想到 AC 自动机有关 \(fail\) 树的路径压缩部分。这里只需要使用 KMP 自动机即可,因为 KMP 自动机实际上就是 AC 自动机在只存储单个字符串时的特殊情况。
相关内容见 KMP 自动机。
原理:求出 \(s\) 的 \(next\) 数组与 KMP 自动机中的 \(to\) 数组后,考虑每次往 \(s\) 结尾添加一个新的字符 \(c\) 时,求出新位置 \(i\) 的 \(next_{i}\) 与 \(to_{i,j}\)。由于 \(to_{i-1,c}\) 是已知的,显然 \(next_{i} = to_{i-1,c}\);然后,便可以继续用新得到的 \(next_{i}\) 来更新 \(to_{i,j}\)(即二者之间可以互相递推得到),后续添加字符时按照同样的方式处理。注意 \(to\) 数组的正确更新与多查询带来的多测清空问题。
总复杂度 \(O(26 * n + 26 * q * m)\)。

浙公网安备 33010602011771号