【笔记】字符串杂题选讲 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,问题变成单点修改,查询一段到根的链上 某个数的数有多少个。改成区间插入一个数,单点查询集合中 某个数的数有多少个。复杂度 2log。

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

P11150 [THUWC 2018] 字胡串

这是什么东西?

先考虑去和 A+B 比较,就是找 i,j 使得 A[1,i]+B[1,j1]=A[1,i+j1]B[j]<A[i+j],枚举 B 的前缀 B[1,j1] 再加一个 >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 中匹配的段是 s1,则只取出 A[1,2|s1|] 进行匹配,如果没有匹配就删去 A[1,|s1|],如果匹配了就一直删到匹配位置,也就是说我们用 O(mlogm) 的时间删去 Am 个字符,总复杂度自然是 O(nlogn)

P11291 简单的字符串问题 2

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

可以感受到如果记 dpl,kargmaxfl,k,则 dpl,k 的枚举下限可以改为 fl,k1 甚至 dpl,k2,不过我们做这样一件事情,记 posi=argmaxj=itoi+1toj,然后 iposi 连一条边,这样 fl,k1fl,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

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

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

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

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


  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 @   caijianhong  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示