Happybob's String Challenge
Happybob's String Challenge(口胡 ver)
做法来源:我、hb、题解
大致按体感质量分类,内部按题目编号排序。
Good Problems
H. CF1562E Rescue Niwen!
Tag: 动态规划、推性质
显然对于每一个后缀,我们选择的都是它在一个区间中的前缀(可能为空)。
进一步分析,发现我们选择的一定是一个后缀中的前缀。
因此可以 dp,设 \(f_{i, j}\) 表示当前最后的是 \(i\) 后缀的 \(j\) 前缀。有转移 \(f_{i, j} \gets f_{i, j - 1}\)。还有对于字典序严格 \(\gt i\) 的 \(k\),有 \(f_{k, \text{lcp}(i, j)} \gets f_{i, n - i + 1}\)。
K. CF2172H Shuffling Cards with Problem Solver 68!
Tag: 变种 SA
不妨假设原题为 \(0\text{-index}\)。设 \(p_i\) 表示在第 \(i\) 个位置的牌原来的编号,则每次操作相当于把 \(p_i\) 的最低位转到最高位。所以显然它有 \(k\) 的周期。更进一步,我们可以直接预处理出旋转 \(t\) 次的编号 \(p_i\)。
那么如果一开始选择了循环位移 \(j\),则最终位于第 \(i\) 个位置的牌就是 \((j + p_i) \bmod k\)。
考虑对它进行倍增排序。设 \(\mathit{rk}_{i, j}\) 表示从 \(i\) 开始的循环位移,若只关心前 \(2^j\) 个字符,它排第几名。发现倍增只需要把所有 \(i\) 按二元组 \((\mathit{rk}_{i, j}, \mathit{rk}_{i + p_{2^j}, j})\) 进行排序即可。
P. CF1038F Wrap Around
Tag: KMP-AM
考虑判定。建出 \(s\) 的 KMP-AM,然后从 \(0\) 开始用 \(t\) 跑 KMP-AM,假设走到 \(x\)。接着再从 \(x\) 开始跑,最后肯定还回到 \(x\)。如果后面 \(x \leadsto x\) 之间经过了 \(n\),那就是合法的。
因此可以枚举 \(x\),然后在自动机上 dp,容易做到 \(O(n^3)\)。
因为一定要经过 \(n\),所以可以改为从第一个 \(n\) 往两边 dp,可以做到 \(O(n^2)\)。
或者正难则反,计算不包含 \(s\) 的方式,相当于把自动机中 \(n\) 这个点扣掉,然后计算长度为 \(n\) 的环的数量。有科技能做到 \(O(n \text{polylog})\)。
V. AGC026E Synchronized Subsequence
Tag: 贪心
我们先将序列划分成若干个 \(a, b\) 数量相等的子段。可以证明,最终的答案里一个子段要么全不选,要么选择与其他子段独立。接下来考虑对一个子段求答案。
设 \(a_i, b_i\) 表示第 \(i\) 个 \(a, b\) 所在的下标。可以发现一个子段要么全部 \(a_i \lt b_i\),要么全部 \(a_i \gt b_i\)。
对于 \(a_i \lt b_i\) 子段,选出来的开头显然为 \(a\),所以先选上 \(a_1, b_1\)。因为我们已经做到第二个字符是 \(b\) 了,所以 \((a_1, b_1)\) 中的所有 \(a\) 都不选。在 \(b_1\) 之后,因为我们还希望尽量长,所以又是一个类似的子问题。最终能得到一个 \(abab\dots\) 型字符串。
对于 \(b_i \lt a_i\) 子段,可以发现若选择了 \(b_i, a_i\),则再选择 \(b_{i + 1}, a_{i + 1}\) 一定不会劣。因此选择的 \(i\) 一定是一段后缀,可以枚举并判断。
最终变为有若干个字符串,要从中选出若干个按原顺序拼接,使得字典序最大。可以从后往前贪心,能加就加。
Y. ARC175F Append Same Characters
Tag: 字典树、拼接比较引理
可以先做完第一种操作再进行第二种操作。第二种操作所需的次数就是逆序对个数。
对于两个串 \(A, B\),不妨设 \(A\) 比 \(B\) 短。如果在前 \(\lvert A \rvert\) 个字符就有区别了,那逆序关系肯定不会改变了,就不用管了。否则,设 \(B = AY\)。
假设我们插入了 \(X\),易得 \(A, B\) 之间的关系发生变化(也就是 \(AX \gt AYX\))等价于 \(X \gt Y^\infty\)。
所有的 \(Y\) 也就是所有 \(B\) 的后缀(也就是字典序上以某个字符串结尾的祖先后代链)。因为总长是有保证的,所以我们可以对每个 \(Y\) 单独计算它的贡献。也就是计算 \(B\) 在序列中的下标,位于哪些 \(A\) 的后面、前面,可以在 vector 上二分。然后,我们只需要枚举 \(x\) 属于哪两个 \(Y\) 之间就行了。
比较两个 \(Y^\infty\) 之间的大小,可以用拼接引理转化,变为比较 \(Y_1Y_2\) 和 \(Y_2Y_1\)。另外 \(X\) 的最短长度也就是 \(\text{lcp}(Y_1^\infty, Y_2^\infty) + 1 = \text{lcp}(Y_1Y_2, Y_2Y_1) + 1\)。注意特判 \(Y_1Y_2 = Y_2Y_1\) 的情况。
Medium or Harder Problems
E. CF1968G2 Division + LCP
Tag: Z 函数、调和级数、序列并查集
注意到第一个串是前缀,所以这些 LCP 只有 \(O(n)\) 种取值。考虑枚举 LCP 的长度 \(l\),那么一个前缀 \(i\) 合法当且仅当 \(Z_i \ge l\),也就是合法的是一个区间。
因此我们可以按 \(l\) 从小到大扫描,每次 ban 掉 \(Z\) 太小的位置,然后用序列并查集贪心地求出最多能划分成多少段。最后再将得出来的东西转置一下就得到答案了。
如果把并查集改成启发式合并,就能得到 \(O(n\log n)\) 的做法。
G. CF1550E Stringforces
Tag: 状压 dp
注意到 \(k\) 很小,猜测复杂度中 \(k\) 是指数。
考虑二分答案 \(d\),然后可以从左往右 dp。设 \(f_S\) 表示,要使 \(S\) 这些字母都选出了长度为 \(d\) 的子串时,左端点最小是多少。check 时需要对于一个字母 \(c\) 和位置 \(l\),快速找到第一个在 \(l\) 右侧,只含 \(c\) 和 ? 的区间。可以预处理出来。具体相当于一个字母会把别的字母包含这个位置的区间都 ban 掉。
I. CF1437G Death DBMS
Tag: 失配树、树剖
子串也就是前缀的后缀。也就是在跑 ACAM 的时候,每个节点在 fail 树上的所有祖先。
于是问题变为,树上单点修改,求到根路径 \(\min\)。树剖线段树维护即可。
当然也存在聪明的办法可以离线之后扫描线少一个 \(\log\),我不会。
M. CF1827C Palindrome Partition
Tag: 贪心
可以发现若一个串可以划分成若干个偶回文串,则一定能通过贪心选最短的方式划分。原因是若一个偶回文串存在前缀是偶回文串,则能对称地将其分割。
因此建出转移的树就行了,答案为每个点的深度之和。
Q. CF2092F Andryusha and CCB
Tag: 调和级数
观察到我们只需要知道一个连续段长度是 \(=1\) 还是 \(\ge 2\),分别称为 \(1, 2\) 连续段。
枚举最终的美好度 \(c\),则当 \(c \ge 2\) 时,所有 \(1\) 连续段只会被恰好一个包含,\(2\) 可能被拆成两半。
接着枚举划分的段数 \(k\)。我们可以贪心地求出能划分成 \(k\) 段的最短、最长前缀 \(l, r\),则区间 \([l, r]\) 中每一个前缀都可以划分成 \(k\) 个美好度为 \(c\) 的子串。容易通过调整法说明(将最后一个分裂的 \(2\) 完整归为左边即可使 \(r\) 增加 \(1\))。
可以证明对于一个字符串,若确定了每一段的美好度 \(c\),则划分的段数是固定的。因此上面的方法不会计重。
\(k, c\) 的段数显然是调和级数,所以总复杂度为 \(O(n\log n)\)。
Plain or Easy Problems
A. ABC284F ABCBAC
Tag: 哈希
注意到 \(S\) 长度固定,所以可以枚举 \(i\) 然后用哈希判断。
B. ABC377G Edit to Match
Tag: 字典树
我们可以先加入一个空串。两个字符串的距离就是字典树上的距离。直接在树上维护即可。
C. CF1729G Cut Substrings
Tag: 哈希、动态规划
显然可以动态规划。设 \(f_{i, j, k}\) 表示当前已经处理完 \(s\) 的前缀 \(i\),在接下来 \(j\) 个字符中必须要移除一次 \(t\) 的出现(否则更早的出现就消除不掉了),已经移除了 \(k\) 次。直接转移即可做到 \(O(n^3)\)。
D. CF1721E Prefix Function Queries
Tag: KMP-AM
原题相当于我们动态加入数,求新的部分的 KMP 数组。但是 KMP 的复杂度是均摊的,这里显然不能暴力求。于是可以用 KMP-AM 来代替,即预处理出失配的转移。
Strange or Dull Problems
F. ABC272F Two Strings
Tag: SA
SA 板子?
L. ABC312H snukesnuke
Tag: 周期
不难求出所有串的最小整周期,不同周期的串之间互不影响。于是问题变为,维护集合 \(S\),每次加入一个数 \(x\),找到不在 \(S\) 中最小的 \(x\) 的倍数加入 \(S\)。
因为加入的 \(x\) 的总和是有保证的,所以可以对每个 \(x\) 都开一个序列并查集来维护。
S. CF932G Palindrome Partition
Tag: PAM
原题的划分,等价于在字符串 \(s_1s_ns_2s_{n - 1}\dots\) 上进行偶回文子串划分。
然后就是在 PAM 上,借助“回文 border 引理”来进行转移。
W. CF1815E Bosco and Particle
Tag: 动态规划
混入其中的非字符串题。本题唯一字符串部分就是求出每个串的最小周期。
首先把所有 \(s_i\) 换成最小周期,这显然不影响答案。观察到穿过 \(i\) 的次数是 \(s_i\) 中 \(0\) 的个数,所以如果有奇数个的话,我们再执行 \(s_i \gets s_is_i\)。
我们可以对每个 \(i\) 计算,单看 \(s_i\) 这个门,在一个周期中向左、向右发射粒子的次数 \(a_i, b_i\)(其实就是不断累计,每遇到一个 \(0\) 就改为在另一个数组中 \(+1\))。
考虑从后往前 dp。设 \(T_i\) 表示 \(i\) 到后面所有门,需要从左侧射入几次粒子才形成周期,\(f_i\) 表示期间需要的总时间。
当加入门 \(i\) 时,显然有 \(a_i \mid T_i\)。另外还有 \(T_i \times \frac{b_i}{a_i} \mid T_{i + 1}\)。因此有 \(T_i = \text{lcm}(a_i, T_{i + 1}a_i / b_i)\)(当然后面这个可能是小数,我们直接把它的分母忽略就行了)。\(f\) 就是 \(f_i = 2T_i + \frac{T_ib_i}{a_iT_{i + 1}}f_{i + 1}\)。
维护 \(\text{lcm}\) 可以直接维护各个质数的指数,乘除的话用 \(\bmod 998\) 意义下的逆元就行了。
Sh*t Problems
J. CF1466G Song of the Sirens
???
N. CF1310C Au Pont Rouge
???
O. CF1923F Shrink-Reverse
注意到翻转次数不超过 \(1\) 次。
R. CF2201E ABBA Counting
Tag: 卷积、容斥
先考虑一个相对简单的问题:对划分方案计算数量。
显然 \(A+B\) 的总和是确定的。\(A+B\) 与 \(B+A\) 的关系是循环位移,所以我们要计算的就是,对于每一个后半部分的循环位移,计算与前缀匹配的方案数。
将后半部分反过来并复制一遍,可以发现对应位置相等变成了下标和固定的位置相等。于是可以用卷积算出贡献。
但原题是对 \(S\) 计数。考虑容斥。一个 \(S\) 会被计算多次,当且仅当它存在对应的周期。所以只需要对每个周期分别计算一次即可。
T. CF1535F String Distance
Tag: 平衡复杂度
首先两个串若能转化,则字符集要相同。对于一个等价类内的两个串,显然距离不超过 \(2\)(可以对两个都排序一次)。所以我们只需要计算次数为 \(1\) 的对的数量。
显然我们会在字典序较大的那个串选择一个区间进行排序。
一种 \(O(n^2)\) 的求法是,枚举两个串 \(s_1, s_2\),计算它们的 LCP, LCS,中间的部分就是需要排序的。我们只需要检查中间的部分是否有序就行了。
还有一种 \(O((\sum L)^3 / n^2)\) 的求法,就是枚举一个串上的子区间,计算排序后有几个串和它相同。
因此在一个等价类的 \(n \le (\sum L)^{\frac 34}\) 时采取第一种,否则采取第二种即可平衡。
X. CF1598G The Sum of Good Numbers
Tag: 哈希
因为不存在 \(0\) 的性质,所以最高的进位不超过较短数的最高位 \(+1\)。所以较长串的长度要么是 \(\lvert x\rvert\),要么是 \(\lvert x\rvert-1\)。
然后枚举即可。
Z. ABC280H Substring Sort
Tag: SA
又来一个板子。
Other
U. CF2249E2 String
不会。


浙公网安备 33010602011771号