后缀自动机练习专题

后缀自动机练习专题

一些比较有用的东东:

  • (1) \(\text{sam}\) 上一条从初始状态出发的路径对应一个子串html

  • (2) \(\text{parent}\) 树上一个节点能表示的最长的串对应一个前缀/后缀算法

  • (3) \(len(u)\) 表示节点 \(u\) 能表示的最长串的长度优化

  • (4) \(fa(u)\) 表示节点 \(u\) 的后缀连接指向的节点,也就是其在 \(\text{parent}\) 树上的父亲spa

  • (5) 表示两个后缀的公共前缀的节点是两个后缀在 \(\text{parent}\) 树上的 \(lca\)指针

  • (6) \(R(u)\) 表示节点 \(u\)\(\text{right}\) 集合,\(sz(u)\) 表示 \(R(u)\) 的大小,\(R(u) \subset R(fa(u)), sz(u) < sz(fa(u))\)code

  • (7) 广义 \(\text{sam}\) 能够理解为一个 \(\text{sam}\) 维护多个串,每一次插入完一个串后将 \(tail\) 指针设为初始状态htm


SPOJ 1811 Longest Common Substring

给出两个串 \(s, t\),求这 \(s, t\) 的最长公共子串,\(|s|, |t| \leq 2.5 \times 10^5\)blog

​ 对于 \(s\)\(\text{sam}\) ,根据 (1) 能够将 \(t\) 放进 \(\text{sam}\) 匹配,当失配时至关于要要从当前匹配到的串的一个后缀继续开始匹配,等价于在 \(\text{sam}\) 上跳 \(fa(u)\) 。能够证实这样必定能匹配到最长公共子串。字符串


SPOJ 1812 Longest Common Substring II

给出 \(n\) 个串,求这个 \(n\) 个串的最长公共子串,\(1 \leq n \leq 10, |s| \leq 10^5\)get

​ 和上题相似的, 仍是取出一个串建 \(\text{sam}\),能够求出每个节点被多少个串匹配到了,那么答案就是 \(\max(len(u))\)\(u\) 是被 \(n - 1\) 个串匹配到的节点。考虑每一次匹配到某个节点的时候,根据 (6),其祖先表明的串也会被匹配到,因此每个串匹配完之后还要更新其祖先的匹配次数,暴力向上跳便可。

​ 另一种作法,考虑串数只有 \(10\) ,能够建一个广义 \(\text{sam}\) 将全部串放进去,用二进制维护 \(R(u)\) 是否包含来自某个串的后缀,当一个节点包含来自全部串的后缀时,其就能够对答案产生贡献。当 \(n\) 比较大的状况能够用 \(\text{bitset}\) 或者线段树合并来维护。


给出一个串 \(s\),以及\(q\) 次询问,每次询问 \(s\) 中第 \(k\) 小的子串,重复的子串仅算一次, \(|s| \leq 9 \times 10^4, q \leq 500\)

​ 能够不考虑 \(\text{parent}\) 树,只考虑 (1) ,计算出每个节点向下走能走到的路径数量。因为要求的是字典序第 \(k\) 小,每一次二分出从这个节点出发应该走的转移边的字符是什么,向下走便可。这样作复杂度是 \(O(|s|q)\),因为 \(q\) 不大,足以经过此题。

​ 实际上考虑 \(\text{parent}\) 能够进一步优化算法的复杂度,考虑原先的 \(\text{parent}\) 树一个节点表明的多个串都是最长的串的一个后缀,是一棵相似于前缀树的结构,这样不能适用于一些字典序上优美的性质。不妨将串反序插入到\(\text{sam}\) 中,这样每个点能表明的多个串都是最长的串的前缀,这些串从长到短在字典序上必定是有序的。扩展到整棵树上,根据 \(mn(u) = len(fa(u)) + 1\) ,每一个点表明的字符串都比其祖先表明的字符串的字典序大。因而能够计算出每一棵子树表明了多少串,在 \(\text{dfn}\) 序上二分答案便可,复杂度是 \(O(qlogn)\)


「TJOI2015」弦论

给出一个串 \(s\) ,根据题目要求来求出相同子串算一个或多个的第 \(k\) 小子串 \(|s| \leq 5 \times 10^5\)

​ 第一问和上一题彻底等价,考虑第二问的状况,一个子串在 \(s\) 中的出现次数就是其对应 \(\text{parent}\) 树节点的 \(\text{right}\) 集合大小,也就是 \(sz(u)\) ,只须要对于每一条路径把权值为 \(sz(u)\) 进行统计便可。


Codechef January Challenge 2018 - Killjee and k-th letter

给出一个的串 \(s\),将 \(s\) 全部子串按照字典序排列好相接起来造成一个新串,\(q\) 次询问,每一次询问问新串中的第 \(k\) 个字符是什么,强制在线。\(|s|, q \leq 2 \times 10^5\)

​ 考虑上上题给基于 \(\text{parent}\) 的作法,反序插入后每一个节点表明的字符串在 \(\text{dfn}\) 序上是有序的,因此只要求出每个节点表明的串的数量,而后对 \(\text{dfn}\) 序进行二分求出答案在哪一个节点表明的子串上。考虑一个子串其所表明的串长度是从 \([len(fa(u))+1,len(u)]\) 连续的,能够直接求出 \(k\) 对应的子串是从节点 \(u\) 对应的后缀的多少位,查找该位置在原串上的字符便可

Codeforces 873 F. Forbidden Indices

有一个串 \(s\)\(s\) 的有些位置是非法的,求全部不以非法位置为结尾的子串 \(a\)\(\sum|a|\times tot(a)\) 的值,其中 \(tot(a)\) 表示 \(a\)\(s\) 中的出现次数。\(|s| \leq 2 \times 10^5\)

\(\text{parent}\) 树上一个节点表明的串的总数就是 \((len(u)-len(fa(u))) \times sz(u)\) ,维护出每个串不含非法位置的 \(\text{right}\) 集合大小后直接求和


「AHOI2013」差别

给出一个串 \(s\) ,对于其全部后缀 \(t_i, t_j\) ,求 \(\sum len(t_i)+len(t_j) - 2len(lcp(t_i, t_j))\) 的值, \(|s| \leq 5 \times 10^5\)

​ 考虑将串反序插入到 \(\text{parent}\) 树后,两个后缀节点的 \(\text{lcp}\) 就是他们的 \(\text{lca}\) ,那么能够树形 \(\text{dp}\) 出以每个节点做为 \(\text{lca}\) 时获得的 \(\sum len(t_i)+len(t_j) - 2len(u)\) ,最后对全部节点求和就是答案


「NOI2015」品酒大会

给出一个串 \(s\) 和一个序列 \(a\) ,对于全部 \(i\) ,求出 \(lcp(x, y) \geq i\) 的方案数以及知足条件的最大的 \(a_x \times a_y\) \(|s| \leq 3 \times 10^5|a_i| \leq 10^9\)

​ 本质上和上一题作法同样,考虑第一问只须要求出对于每个节点 \(u\) ,在其子树里选出两个节点 \(\text{lca}\)\(u\) 的方案数便可,第二问稍有复杂,由于权值可正可负,须要分类讨论 \(\text{dp}\) 维护最大值和最小值,便于合并相乘时统计答案


「BZOJ3473」字符串 (双倍经验=3277)

给出 \(n\) 个字符串,求有多少个子串是其中至少 \(k\) 个字符串的子串,\(\sum|s| \leq 10^5, 1\leq k \leq n\)

​ 创建一个广义 \(\text{sam}\) ,每次插入一个串后维护一下 \(\text{parent}\) 树上哪些节点的 \(\text{right}\) 集合已经拥有了来自这个串的后缀,这样每个新增的插入节点都须要向祖先更新这个信息,根据均值不等式分析,插入全部串的总复杂度是 \(O(|s|\sqrt|s|)\)

​ 考虑能够直接用线段树合并维护每个节点的 \(\text{right}​\) 集合有哪些串的后缀,所有建完之后向上合并更新祖先的答案,总复杂度 \(O(|s|log|s|)​\)


「ZJOI2015」诸神眷顾的幻想乡

有一棵 \(n\) 个节点的树,每一个节点上有一个字符,如今把每一条简单路径当作一个串,求本质不一样的子串数量,\(n \leq 10^5\) 树的叶子节点数量 \(\leq 10\)

​ 若是一条简单路径被另一条简单路径包含,那么其表明的串彻底能够当作另一个串的子串,因此咱们只须要考虑不被任何串包含的简单路径,其数量显然是叶子节点个数的平方。因而暴力将这些串插入到广义 \(\text{sam}\) 中对不一样子串数量计数便可。


「SDOI2016」生成魔咒

一开始有一个空串,一共有 \(n\) 次操做,每一次操做在这个串末尾加一个字符,求每一次操做结束时串中不一样子串的数量 \(n \leq 10^5\)

\(\text{sam}\) 是在线构造的,每一次插入节点 \(p\) 后维护一下 \(len\) 发生改变的节点对答案的贡献便可


「BZOJ2555」 Substring

在当前字符串的后面插入一个字符串
询问字符串 \(s\) 在当前字符串中出现了几回?(做为连续子串) 你必须在线支持这些操做。

​ 丧心病狂的一道题,因为在线插入串到 \(s\) 后还要维护每个点的 \(sz(u)\) ,因此要动态支持给 \(\text{parent}\) 树加边,维护子树大小能够转为链加,用 \(lct\) 来维护便可


Codeforces 666 E. Forensic Examination

给你一个母串和不少询问串,每次询问求母串的一段区间在一段连续询问串中出现最多的询问串的出现次数和编号 \(1 \leq |s|, q \leq 5 \times 10^5\)

​ 对全部串建一个广义 \(\text{sam}\) ,每次询问倍增找到左端点在 \(\text{parent}\) 树上对应的节点,用线段树合并维护出每一个节点的 \(\text{right}\) 集合中每一个询问串的出现次数,区间查询 \(\max\) 便可


「TJOI / HEOI2016」字符串

萌萌哒的传送门= =


「NOI2018」你的名字 (非正解)

萌萌哒的传送门= =


「BJWC2018」 Border的四种求法

萌萌哒的传送门= =

相关文章
相关标签/搜索