简单字符串知识点做题笔记

manacher

P9646 [SNCPC2019] Paper-cutting

观察到如下两个性质:

  1. 对于同一个折叠方向的所有可行折叠棱,我们能折叠就一定折叠,因为折叠不会导致可行的折棱变得不可行,但可能使某些更后方的不可行的棱变得可行。
  2. 不同方向的折叠操作互不影响,即一次折叠不会导致不同方向上的某个可行折叠棱变得不可行;也不会使得某个不可行折叠棱变得可行。因此四个方向可以独立求解。

我们考虑实现 cut 和 rotate 两个函数,分别表示将目前的矩形的右侧尽可能向左内折、以及将当前矩形顺时针旋转 \(90^{\circ}\)。重复执行这两个操作 \(4\) 次,我们就可以完成四个方向的折叠。

rotate 的实现是容易的。考虑如何实现 cut 函数。我们先对每一行跑 manacher,求出每一个间隙对应的最长回文半径,\(d_i\) 表示 \(i\) 这一列右侧的空隙对应的最长回文半径。再对同列的 \(d_i\)\(\min\),得到一条完整间隔的最长回文半径 \(mind_i\)。考虑从右往左扫描每一列,动态维护当前矩形的右边界对应列数 \(r\),每当我们扫到一个 \(i+mind_i\geq r\),就说明可以以 \(i\) 这一列右侧的空隙作为折叠中心进行折叠,直接令 \(r\gets i\) 即可。

得到最终的折纸形态后,BFS 求出 \(0\) 的连通块个数即可。

P8631 [蓝桥杯 2015 国 AC] 切开字符串

\(f(l,r)\) 表示区间内本质不同奇回文串数目,\(g(l,r)\) 表示区间内本质不同子串数目。求出这两个之后,求答案是容易的。

考虑 \(g\):我们要求的是一个后缀的 \(g\),本质不同子串考虑 SA,是总子串减去 rk 相邻两个串的 LCP。每次拓宽一个后缀等价于插入一个新的后缀,用 set 维护,每次插入时在 ST 表上求 LCP 更新答案即可。

考虑 \(f\):我们只需要求一个前缀或后缀的答案。注意到本质不同回文串数目是 \(O(n)\) 的,在 manacher 过程中,每次最右侧的回串右端点往右扩展时,会新生成一个可能不同的回文串。可以利用哈希进一步判断,也可以基于求出来的 SA 做:对于每一个长度开一个 set,set 内按照 rk 排序,加入时找到前驱后继看 LCP 是否大于等于子串长度即可。一个串求出来之后,加在对应位置,最后做前缀和累加即可。

ACAM

  • insert 建 Trie;
  • Trie 上 BFS,没有 ch 的根据自己的 fa 新连 ch(fa 的 ch),有 ch 的等价于碰到一个新点,根据自己的 fa 给它连 fa(自己 fa 的对应 ch 边是指向点的 fa)。
  • 性质:第 \(i\) 个字符加入后走到的位置,在 fa 树上到根的链上每一个点都可以 \(i\) 为结尾匹配(或叫出现)。

P5840 [COCI2015] Divljak

在线实现加一个文本串、查一个模式串在多少个文本串中出现过。注意这里不是求次数和。对模式串建立 AC 自动机,每次插入一个文本串就把所有走过的点拿出来,我们要对这些点与根形成的连通块内的点 \(+1\)(树链求并)。于是按照 dfs 序排序,考虑到 \(p_i\) 时,给 \(1\sim p_i\)\(1\),再给 \(1\sim lca(p_i,p_{i-1})\)\(1\) 即可。转成单点加子树查,用树状数组做。

P8451 [LSOT-1] Crosspain

可持久化 ACAM,然而支持离线。考虑离线建立操作树,需要支持加入/删除/匹配一个字符串。不妨把所有加入的字符串一起建立 ACAM,加入和删除等价于单点修改,匹配等价于链求和,差分后上树状数组即可。

P9613 [CERC2019] K==S

AC 自动机,上面有一些点不能走。在 fail 树上 dfs 求出哪些点不能走,然后用矩阵快速幂优化 dp 即可。

P9935 [NFLSPC #6] 啊,忘记了。

复杂分类讨论+Trie 上数据结构+ACAM。码量比较大的题。

QOJ1942. Another Substring Query Problem

题意:\(q\) 次询问,每次询问一个串 \(t\)\(s\) 中第 \(k\) 次出现的位置。

离线,对询问串建立 AC 自动机,\(s\) 进去匹配,走到的点打一个 tag,每个点上有询问形如第 \(k\) 小的 \(tag\),线段树合并即可。

SA

QOJ5312.Levenshtein Distance

总共有 \(O(nk)\) 个可能的串,对于每个串执行判定 dp,设 \(f_{i,j}\) 表示匹配到 \(S\)\(i\)\(T\)\(j\) 的最小编辑距离,枚举起点,将 \(O(k)\) 个同时做可以做到 \(O(n^3)\)。注意到 \(|i-j|\leq k\),于是记录差值,设 \(f_{i,j}\) 表示使得 \(S[1\sim i]\)\(T[1\sim i+j]\) 匹配的最小编辑距离,可以做到 \(O(n^2k)\)。尝试交换状态与答案,你发现对于一个固定的 \(j\),编辑距离关于 \(i\) 单调不降,因此再设 \(f_{i,j}\) 表示编辑次数为 \(i\),能使得 \(S[1\sim x]\)\(T[1\sim x+j]\) 匹配的最大的 \(x\),转移可以仅在编辑处进行,即对于 \(f_{i,j}=x\),枚举 \(S[x+1]\)\(T[x+j+1]\) 的匹配方式,有跳过某一个或将两个改成相等的三种方案;然后还需要更新接下来两个位置的 LCP,这些位置本身就是匹配好的。预处理 SA 和 ST 表可以 \(O(1)\) 转移。

\(f\) 求出来之后,对于 \(O(k)\) 个可行位置的每一个 \(j\),从小往大枚举 \(i\),求出最小的操作次数使得 \(f_{i,j}=n\) 即可。

posted @ 2025-03-02 16:37  烟山嘉鸿  阅读(34)  评论(0)    收藏  举报