【笔记】字符串杂题选讲 2025.2.6

【笔记】字符串杂题选讲-范斯喆 2025.2.6

已完成 2 题代码:CF1975G、CF1975H

CF1827C Palindrome Partition

将所有偶回文串分解为极小的偶回文串,则分解的方案是唯一的,假如可以对所有 \(i\) 求出 \(S[1, i]\) 的最小的偶回文串后缀,则可以 DP 解决。

可以使用 manacher,只需要求 \(i\) 前面最右的一个包含 \(i\) 的回文中心。从大到小枚举回文中心并使用并查集的区间赋值技巧即可快速完成。

CF1913F Palindromic Problem

对于每个回文串,判断它什么时候能/不能计入答案。枚举回文中心,先考虑在回文半径中的回文串,是若干区间加等差数列;再考虑由修改回文半径外第一个字符产生的新的回文串,只有一个,可以暴力考虑贡献。

需要使用后缀数组求 LCP。

P10716 简单的字符串问题

建立 border 树,枚举答案是一段前缀 \(A=S[1,b]\),然后可以在 \(b\) 的子树里从 \(b\) 开始一步步匹配(除了末尾都是固定的),复杂度是调和级数乘 dsu on tree。

查询时从小到大扫描 \(i\),问题变成单点修改,查询一段到根的链上 \(\geq\) 某个数的数有多少个。改成区间插入一个数,单点查询集合中 \(\geq\) 某个数的数有多少个。复杂度 2log。

而事实是,可以发现答案一定取在 border 树上的一段到根的链,所以树套树可以改成树上倍增。而预处理的部分可以用 Z 函数(扩展 KMP 算法)优化,即若当前匹配到 \(i\),则下一次需要找 \(p>i\)\(S[p,p+b-1]=S[1,b]\),然后 \(i=p+b-1\)。从小到大枚举 \(b\) 并逐渐删掉 \(z_p <b\) 的点能帮助我们快速找到合法的 \(p\)

P11150 [THUWC 2018] 字胡串

这是什么东西?

先考虑去和 \(A+B\) 比较,就是找 \(i, j\) 使得 \(A[1,i]+B[1,j-1]=A[1,i+j-1]\)\(B[j]<A[i+j]\),枚举 \(B\) 的前缀 \(B[1, j-1]\) 再加一个 \(>B[j]\) 的字符在 \(A\) 上匹配就能找到一个 \(i\)。这样以后我们取出 \(i+j\) 最小的,这些 \(i, j\) 有可能是答案,再去从他们里面找最小的一种组合,这时就只需要比较两个 \(B[j+1,m]\),是后缀排序。最后找到了最优的 \(i, j\) 之后只能说明最终答案中的 \(i\) 至少是刚才求的 \(i\),但是它可以往左再移动一点,具体是移动 \(B\) 的最小循环节长度。然后写一个二分哈希快速移动 \(i\) 就行了。

CF1975G Zimpha Fan Club

通过一系列比较,将两个字符串的两个头、两个尾都尽可能匹配掉,直到遇到 * 停止。如果此时两个字符串都有 * 就输出 Yes,然后判一下有串空掉的情况,现在就剩下一个字符串没有 *(记作 \(A\)),另一个字符串的头和尾都是 *(记作 \(B\))的情况。

如果没有 -,那么这是一个 KMP 匹配问题,将有 * 的字符串按照 * 分段,用 \(A\) 一段段匹配,一旦失配就让他失配,直到匹配满了立即跳到下一个。总之就是分段匹配。

如果有 -,问题的瓶颈变成怎么匹配一个段并要求匹配位置尽可能靠前。这就是通配符匹配,匹配一段需要一次 FFT。

为了确保复杂度正确,假如当前 \(B\) 中匹配的段是 \(s_1\),则只取出 \(A[1,2|s_1|]\) 进行匹配,如果没有匹配就删去 \(A[1,|s_1|]\),如果匹配了就一直删到匹配位置,也就是说我们用 \(O(m\log m)\) 的时间删去 \(A\)\(m\) 个字符,总复杂度自然是 \(O(n\log n)\)

P11291 简单的字符串问题 2

以为是无意义堆难度,没想到是有意义的。令 \(to_i\) 表示最大的 \(j\) 使得 \(T[i, j]\) 是好的串(注意 \(n\) 小的有一点惊人),然后固定 \(l, k\)\(r\) 是一段左端点为 \(l\) 的区间(注意空串也是好串),记作 \(dp_{l, k}\),然后有转移,它 \(=\max_{i=l}^{dp_{l, k-1}+1}to_i\),另外 \(dp_{l, 1}=to_l\)。然后就可以统计答案。

可以感受到如果记 \(dp_{l, k}\)\(\arg\max\)\(f_{l, k}\),则 \(dp_{l, k}\) 的枚举下限可以改为 \(f_{l, k-1}\) 甚至 \(dp_{l, k-2}\),不过我们做这样一件事情,记 \(pos_i=\arg\max_{j=i}^{to_i+1}to_j\),然后 \(i\)\(pos_i\) 连一条边,这样 \(f_{l, k-1}\)\(f_{l, k}\) 就是跳一步 \(pos\)。这是基环树森林,理论可做。

CF1975H 378QAQ and Core

我看不懂,但我大受震撼.jpg

https://www.luogu.com.cn/article/dtv8w2bs

https://www.luogu.com.cn/article/04teche7

CF2053G Naive String Splits

太牛了,首先假设我们要判定的两个字符串 \(s_1, s_2\) 满足 \(|s_1|<|s_2|\),我们做贪心,优先匹配短串,匹配不了的时候就回退考虑长串如何填。为了更好的利用信息考虑先找到 \(k, c\) 使得 \(s_2=s_1^kc\)\(s_1\) 不是 \(c\) 的前缀,这样就能快速回退了,使用哈希手段可以实现。

但是这个做法是很危险的,事实上也是,对于 \(s_1=aba,s_2=abaab,T=abaababa\) 会爆炸。如果坚持使用贪心则考虑能不能反悔,这个例子就是反悔一次的;再考虑反悔两次或更多次的时候,以两次为例,会出现 \(s_1^{k+2}\)\(s_1^kcs_1\) 都是 \(T\) 的前缀的情况(注意,由于动用了反悔,因此 \(k>0\) 是必然),即 \(cs_1\)\(s_1^2\) 有一个是另一个的前缀。已知 \(s_1\) 不是 \(c\) 的前缀,那么可以知道 \(c\)\(s_1\) 的前缀,那么可以说 \(cs_1=s_1c\),那就完蛋了,这种情况可以出现,出现时会有一个 \(\gcd(|c|, |s_1|)\) 的循环节。解决办法,提前找到原串的最小循环节,特判刚好取到循环节的情况,那样的情况是十分简单的,跑 exgcd 或者好像枚举都对。

复杂度观察一下可以发现只有调和级数 \(O((n+m)\log n)\),十分感人。

题解怎么还有一页,原来可以线性。将 \(s_2\) 分解为 \(s_1^kc\) 时可以二分,复杂度 \(\sum_{i=1}^n\log(n/i)=O(n)\)。然后是匹配 \(T\) 串的部分,如果二分能匹配多少个短串,你理解一下,最坏情况下会升复杂度一个 \(\log\),解决方法是预先倍增出二分上界。还有另一个解决方法类似根号分治,先二分匹配有多少个 \(s_1^{n/|s_1|}\) 再匹配 \(s_1\),这个阈值好像说可以保证复杂度。


  1. 首先,对(\sum_{i = 1}^{n}\log(\frac{n}{i}))进行化简:
    • 根据对数运算法则(\log(\frac{a}{b})=\log a-\log b),则(\sum_{i = 1}^{n}\log(\frac{n}{i})=\sum_{i = 1}^{n}(\log n-\log i))。
    • 进一步展开得到(\sum_{i = 1}^{n}\log n-\sum_{i = 1}^{n}\log i)。
    • 因为(\sum_{i = 1}^{n}\log n = n\log n)((\log n)是常数,(n)个(\log n)相加),所以原式变为(n\log n-\sum_{i = 1}^{n}\log i)。
  2. 然后,对(\sum_{i = 1}^{n}\log i)进行估计:
    • 利用积分估计和式。我们知道函数(y = \log x)在区间([1,n + 1])上是单调递增的。
    • 对于(\sum_{i = 1}^{n}\log i),有(\int_{1}^{n}\log xdx\leqslant\sum_{i = 1}^{n}\log i\leqslant\int_{1}^{n + 1}\log xdx)。
    • 先计算(\int\log xdx),利用分部积分法,设(u=\log x),(dv = dx),则(du=\frac{1}{x}dx),(v = x)。
      • 根据分部积分公式(\int u dv=uv-\int v du),可得(\int\log xdx=x\log x-\int x\cdot\frac{1}{x}dx=x\log x - x+C)。
    • 计算(\int_{1}^{n}\log xdx=n\log n - n + 1)。
    • 计算(\int_{1}^{n + 1}\log xdx=(n + 1)\log(n + 1)-(n + 1)+1=(n + 1)\log(n + 1)-n)。
    • 所以(n\log n - n + 1\leqslant\sum_{i = 1}^{n}\log i\leqslant(n + 1)\log(n + 1)-n)。
  3. 接着,将(\sum_{i = 1}^{n}\log i)的估计结果代回原式:
    • 因为(\sum_{i = 1}^{n}\log(\frac{n}{i})=n\log n-\sum_{i = 1}^{n}\log i),由(n\log n - n + 1\leqslant\sum_{i = 1}^{n}\log i)可得:
      • (\sum_{i = 1}^{n}\log(\frac{n}{i})\leqslant n\log n-(n\log n - n + 1)=n - 1)。
    • 由大(O)记号的定义,若存在正整数(n_0)和正的常数(C),使得当(n\geqslant n_0)时,(f(n)\leqslant Cg(n)),则(f(n)=O(g(n)))。
    • 对于(\sum_{i = 1}^{n}\log(\frac{n}{i}))和(n),取(C = 1),(n_0 = 1),当(n\geqslant1)时,(\sum_{i = 1}^{n}\log(\frac{n}{i})\leqslant n)。

所以(\sum_{i = 1}^{n}\log(\frac{n}{i}) = O(n))。

posted @ 2025-02-06 15:05  caijianhong  阅读(134)  评论(0)    收藏  举报