字符串
sam 实现后缀排序:
我们先翻转,变成前缀排序并且此时字典序从后往前比较,我们建出 sam,对于每个结点 \(i\) 向父亲连的边,求出 \(s[endpos_i-len_{fa_i}]\),其中 \(endpos_i\) 为任意一个出现过的位置,作为它向父亲的边权值,做 dfs,每次走权值小的边,dfn 序就是字典序排序。
先翻转,建 sam,每个子串找到对应的 endpos,放一个代表它的结点,每个endpos 再放一个 \(len_{fa_i}+1\) 和 \(len_i\) 然后再依靠 parent 树连一些边表示它在后面加了一些字符,判环跑最长路。
建出 sa,答案具有可二分性,先二分,从 \(c\) 所在 sa 位置开始二分出最大的一个区间满足区间内所有 \(height_i\ge mid\),我们就找到了那个串所在的 sa 区间,查询这个区间内有没有在 \([a,b]\) 内出现过的,可以主席树。
重要性质:一个串的本质不同回文串数量 \(\le n\)。
证明是每次往后面加一个字符,考虑以这个字符结尾的回文串,除了最长的那个都可以对称到前面从而说明已经出现过。
manacher 的过程实际上就求出了所有本质不同回文串,每次 \(r++\) 的时候都代表一个不同的。
我们的问题在于怎么查询一个串出现次数,使用 sa 可以先找出这个后缀所在 sa 数组位置,二分出一个最长的 \(h_i>=len_s\) 的区间,出现次数为区间长度。
你的名字:
其实和上面的字符串是类似的,我们考虑对于每个前缀求它在区间内出现的最长后缀,可以在 sam 上走路,使用线段树合并维护 endpos,区间查询即可知道在区间内出现长度,从一个点到根一定是先递增再递减,我们如果递增就一直向上跳,就可以 \(O(n\log n)\) 对于每个前缀求这个东西了。
求贡献可以后缀数组求 height,但是每个串单独做会超级慢,拼接起来只跑一次。
ac 自动机提。
我记得讲过一道路径超级长的,那道是倍增优化走路和打标记求和。
这个是自动机超级大,我们如果把所有区间内的子串拿出来见肯定会爆炸。
但是注意性质:有很多满 \(10\) 叉树,那么我们如果走到一个十叉树的根节点,接下来怎么走都有匹配,并且贡献都是一样的,于是我们可以提前处理这个贡献。
类似数位 dp 的,能随意选的一段后缀就对应了一棵十叉树,我们类似数位 dp 的方式维护,设 \(g_{i,j}\) 表示在节点 \(i\),再走 \(j\) 步必定会有的贡献,那么我们就把十叉树拆掉了,结点数可以接受,建完后 \(g_i\) 还要加上 \(g_{fail_i}\)。
那么我们就可以 dp 了,设 \(f_{i,j}\) 表示长为 \(i\) ,当前在 \(j\) 的最大收益,转移 \(f_{i+1,tr_{u,x}} \leftarrow f_{i,u}+\sum_{l}^{n-i}g_{tr_{u,x},l}\),维护前缀和,容易转移。
洛谷题解区怎么都在说不能差分贡献啊太坏了,贡献就是可以差分的。

浙公网安备 33010602011771号