不错的 Z 函数练习题。
很明显正着做不好搞,因为假如选取了当前某个前缀,很容易影响到后面的选取,并且不好优化。
经典正难则反的思想,考虑从后面逐渐选取模式串的前缀,这样就不会影响到前面前缀的选取。也就意味着,我们每次尝试选取一个文本串的一个后缀,使其等于模式串的前缀,然后进行下一步操作。
这是我们想到什么? Z 函数不就是用来求出这东西的吗。考虑 DP,我们很容易写出状态转移方程:
有了这个,直接暴力 DP 是 $\mathcal O(n^2)$ 的,显然可以用线段树来维护,但 $\mathcal O(n\log n)$ 复杂度仍不允许我们通过此题。
还能继续优化吗?注意到对于所以可以转移到的 $f_i$ 一定是具有单调性,因为较短的后缀的代价一定不会大于较长的后缀的代价。而且根据 Z 函数 的性质,对于所有可以转移到的 $i$,$i+p_i$ 明显是单调递减的。这就转化为经典模型了,我们很容易用单调队列来优化,于是复杂度就变为了优秀的 $\mathcal O(n)$。
因为 $f_i$ 具有单调性,每次我们直接将其加入队尾就可以了。然后注意一些细节就可以了。
Code
1 |
|
希望没有误人子弟 qwq。