题目
我并没有在 O J OJ OJ 上找到原题。所以贴图片吧(老师的 PPT \text{PPT} PPT 中的)。
数据范围与约定
强制在线。操作数不超过
2
×
1
0
5
2\times 10^5
2×105 ,字符集大小不超过
1
0
5
10^5
105 。
思路
简化题意:有一棵 trie \text{trie} trie 树,每次新增一个叶子,求最长 border \text{border} border (众所周知,最长 border \text{border} border 长度与最短循环节长度相加,得到原字符串长度)。
最长 border \text{border} border 不就是 K M P KMP KMP 的失配指针吗?而且总复杂度是 O ( n ) \mathcal O(n) O(n) 的?
然而, K M P KMP KMP 的复杂度是均摊的。对于新加入的叶子,它可能需要计算 O ( n ) \mathcal O(n) O(n) 次(假如它的祖先都是一步到位)。
这里我们就要使用奇技淫巧了(甚至,可以用于普通的 K M P KMP KMP 来优化一丢丢)。
对跳失配指针的过程进行优化。不妨记 f ( x ) f(x) f(x) 为失配函数。将 t r i e trie trie 上面的那一条链抽象出来,得到一个字符串。也就是说,幻想我们在进行 K M P KMP KMP 匹配。
- 如果 f ( x ) ≤ ⌊ x 2 ⌋ f(x)\le\lfloor\frac{x}{2}\rfloor f(x)≤⌊2x⌋ ,该干啥干啥,跳过去就是了。
- 否则,存在一个循环节 x − f ( x ) x-f(x) x−f(x) 。假如 f ( x ) f(x) f(x) 的后一位仍然不能匹配,那么可以直接跳到第一个循环节中的对应位置。也就是 ( x − 1 ) m o d [ x − f ( x ) ] + 1 (x-1)\bmod[x-f(x)]+1 (x−1)mod[x−f(x)]+1 。
每次至少要跳没 x 2 \frac{x}{2} 2x 个字符,所以复杂度变成了 O ( log n ) \mathcal O(\log n) O(logn) 。
回到 trie \text{trie} trie 上,我们怎么解决“得到第 ( x − 1 ) m o d [ x − f ( x ) ] + 1 (x-1)\bmod[x-f(x)]+1 (x−1)mod[x−f(x)]+1 级祖先”?
很简单,树上倍增。新插入叶子时,再次计算即可。仍然是 O ( log n ) \mathcal O(\log n) O(logn) 的复杂度。
思路二
定义 g ( x , c ) g(x,c) g(x,c) 为,匹配到 x x x ,下一个字符是 c c c ,何去何从。
显然,递推方法是
- 当 x x x 有字符 c c c 作为子节点时,有 g ( x , c ) = s o n ( x , c ) g(x,c)=son(x,c) g(x,c)=son(x,c)
- 否则,有 g ( x , c ) = g [ f ( x ) , c ] g(x,c)=g[f(x),c] g(x,c)=g[f(x),c] ( f ( x ) f(x) f(x) 同上,为失配函数)。
那么,新加入的叶子节点,其 g g g 函数该如何求解?只会使用第二条规则。
现在有一个问题, f ( x ) f(x) f(x) 怎么算?答案是 f ( x ) = g [ f a ( x ) , c ] f(x)=g[fa(x),c] f(x)=g[fa(x),c] ,其中 c c c 是 x x x 相较于 f a ( x ) fa(x) fa(x) 新增的末尾字符。注:此处的 g [ f a ( x ) , c ] g[fa(x),c] g[fa(x),c] 尚未赋值为 x x x ,尽管马上我们就要这么做。
于是,我们用 可持久化线段树 维护 g g g 函数。分三步进行。
- 在 f a ( x ) fa(x) fa(x) 的线段树中找到 f ( x ) f(x) f(x) 。
- 将 f ( x ) f(x) f(x) 的线段树拷贝过来。
- 最后将 g [ f a ( x ) , c ] g[fa(x),c] g[fa(x),c] 赋值为 x x x 。
代码
此题重在思想,而非代码,对吗?