2024 syzx 冬季训练 7 - 字符串
A
最小值是 \(\sum_{i=0}^m \min(n,26^i)\)。期望是 \(\sum_{i=0}^m 26^i(1-(1-26^{-i})^n)\)。
B
题意:动态 exkmp
如果使用 exkmp 是只能求一个静态字符串的 z 数组,下面简述了如何用 kmp 实现一个 动态的 exkmp。
kmp 可以做到动态向后添加元素并且能求出每个前缀的最长 border,记 borderi 为 s[1 . . . i] 的最长 border,下面考虑如何使用 border 数组来求出 z 数组。
令 z_i 表示 i 开始的位置和前缀的最长公共前缀长度,那么 zi 的情况有两种:
第一种情况是 zi 已经定下来了(并且之后不可能再被修改)。
第二种情况是还没有定下来,向后添加一个字符会使得 zi 长度增加 1 / 定下来。
根据上面的分析,只需要知道每次向后添加一个字符会定下来哪些 zi 的值即可,并且每 个值最多被确定一次。
如果我们添加了一个 s[n] = c,对于没有被定下来的 zi ,如果 s[n − i + 1] ̸= s[n] 就可以 将 zi 定为 n − i。 目标是如何快速的找到所有这样的 i,同时满足条件 (s[1 . . . n − i] = s[i . . . n − 1]) 和 (s[n − i + 1] ̸= s[n])。
假设将 i → border_i 连边成一棵树,那么可以知道满足第一个条件的 n − i 一定是 n − 1 在树上的祖先节点。
对第二个条件可以做一个简单的处理,每个点记一下往上跳到的第一个和自己颜色不一样的即可。
由于每个点的 zi 只会被确定一次,所以暴力跳的复杂度是正确的。 剩下的部分是容易的,只需要记录每个时刻 i 没有被定下来的 zj 对应的 bj 的和,和 ai 简单相乘即可。
C
题意为从s和t中分别选一个子串p和q,要求拼接后p + q为平方串。 先讨论p比q长的情况,则需要满足p可表示为aba,q表示为b的形式。
我们n^2枚举p中两个a串的左断点l1和l2,可以发现可行的a的最大长度 是 lcp(S[l1 :], S[l2 :])。b的最大可行长度是 max_[i from 1 · · · |T|] (S[: l2],T[: i])。因此只需要简单的使用 n^2 dp来预处理出lcp和lcs即 可进行答案统计。 另一种p比q短的情况同理,时间空间复杂度都为O(n^2 )
D
需要对于每个开头和每个结尾求出平方串的数量。
统计 AA,我们枚举 A 的长度 len,我们每隔 len 撒一个点,AA 一定恰好经过两个点,通过 lcplcs 可以判断平方串,前缀和统计答案。
E
• 给你一个 2 × n 的字符矩形,你可以从其上任意一个位置出 发向右或向下走走出一条路径。问将路径上经过的所有字符 串起来后能够得到的最长的平方串的长度是多少。这里平方 串指能够写成 S^2 = SS 形式的字符串,如 abcabc、tt。
方便起见,我们将字符矩阵的两行分别写为 S 和 T。此外 为了方便,我们在 S 的末端和 T 的前端分别加上两个新字 符 $1 和 $2(并令 n = |S| = |T|)。则问题转化为在对于 1 ≤ i ≤ j ≤ k ≤ n,S[i, j)T[j, k) 是平方串的条件下求 k − i 的最大值。 • 首先,别忘了 S 和 T 本身就可能包含平方串,故而显然我 们需要对这两个字符串单独求其平方子串。这也意味着能够 解决这一问题的算法必然也能求出任意一个字符串的平方 串。这提示我们从已有的求平方子串算法进行改造。 • 我们考虑这样一个算法:(从大到小)枚举可能的平方串长 度 2d,将 [1, n] 分解为 [1, d], [d + 1, 2d], . . . , [pd + 1, n] 共 ⌈ n d ⌉ 个子区间。如果存在一个长为 2d = k − i 的平方串 S[i, j)T[j, k),则区间 [i, k) 必然完全包含这 ⌈ n d ⌉ 个子区间 中的至少一个。不妨考虑枚举区间 [(p − 1)d + 1, pd],按照 分割点 j 在区间左侧、区间内和区间右侧三种情况进行分类 讨论。
-
j ≤ (p − 1)d + 1:首先求出 T 中包含 [(p − 1)d + 1, pd] 且 周期为 d 的最长字串 [j, k),再向左贪心拓展即可。具体的
-
j = (p − 1)d + 1 − lcs(T[1,(p − 1)d], T[1, pd])
-
k = pd + 1 + lcp(T[(p − 1)d + 1, n], T[pd + 1, n]) • i = j − lcs(S[1, j − 1], T[1, j − 1 + d])
-
判断 k − i ≥ 2d 是否成立即可(注意到这里同时也可以判断 T 中是否含有平方串)
-
-
(p − 1)d + 1 < j ≤ pd:考虑尽可能贪心地让 S 和 T 在 中间匹配更多地位置,具体而言可以转化为判断 lcp(S[(p − 1)d + 1, n], S[pd + 1, n]) + lcs(S[1,(p − 1)d], S[1, pd]) ≥ n 是 否成立即可
剩下的均为对称情况。
F
将 S 划分为若干段,要求所有前一段要比后一段好。A 比 B 好当且仅当 A 字典序比 B 所在后缀小且 A 不是 B 的前缀。
问最多分为几段。
fi 表示从后往前做到 i,最多划分几段,i 转移到 j 的条件为 \(rk_j<rk_i,j-i>lcp(i,j)\)
注意到 \(lcp(i,j)=\min_{rk_j}^{rk_{i}-1}height\),考虑按照 rk 顺序分治。
记 \(H_i\) 表示 i 到 mid 的 height 的 min,条件变成 \(rk_i<rk_j,j-i>\min(H_{rk_j},H_{rk_i-1})\)。
第二个式子等价于 \(j-i>H_{rk_j}\) 或 \(j-i>H_{rk_i-1}\)。
分治双指针即可。
G
题意:若 S 的前 |T| 位与 T 的编辑距离不超过 1,则称 T 是 S 的 almost prefix。给定串 S,T,你要把 S 分成若干段
令 f(i, j) 表示划分 S1S2 · · · Si−1 的所有方案的 \(\binom{n}{j}\) 之和,其中 n 表示对应方案具有 的段数。再令 R(i) 表示最大的下标 j 满足 SiSi+1 · · · Sj 是 T 的允许一次失配的前缀。根 据 \(\binom{n}{j}=\binom{n-1}{j-1}+\binom{n-1}{j}\),有 f(i, j) = ∑ R(i ′)≥i−1 f(i ′ , j − 1) + f(i ′ , j),可得两种类型的转移: f(i ′ , j) → f(i, j) 或者 f(i ′ , j) → f(i, j + 1) (i ′ < i ≤ R(i ′ ) + 1),均可以使用差分前缀和 O(1) 完 成转移。最后根据 n^2 = 2C_n^2 + C_n^1 ,答案为 2f(|S|, 2) + f(|S|, 1)。 求出每个 R(i) 等价于先求一次 LCP,跳过下一位,然后再求一次 LCP。求 LCP 可以使 用二分 +Hash 或者后缀数组。 时间复杂度 O(n log n),也可以做到线性。
H
题意:一个循环串,问它本质不同回文子串的 \(f(t)^2g(t)\) 之和,f 是 t 的出现次数,g 是 t 的长度。
一个串的本质不同回文子串只有 \(O(n\log n)\) 个,二分哈希加 map 全部找出来就好,需要手写哈希表卡常。
I
题意:给定 n 个字符串,q 次给定 26 个小写字母的顺序,问按这个顺序这 n 个字符串排成一行的逆序对的数量。
两个字符串比较大小只取决于第一个不同的位置,用 trie 树枚举所有节点,计算出一个 26*26 的矩阵(不同字母 pair 对逆序对的贡献)。
J
一个数组,把它的所有区间拿出来去重(把区间看成一个字符集为 1e6 的字符串),问去重之后所有区间最大值的和。
后缀排序,找出本质不同的串(按排序后顺序枚举左端点,右端点的位置是一个区间),求区间最大值的和,变成一个经典单调栈问题。
K
定义 occur(t,s) 表示 s 在 t 中出现的次数。
给定字符串 S 和数组 w,定义 \(f(t)=\sum_{i=1}^{n}w_i\cdot occur(t,pre_i)\),其中 pre_i 为 S 长度为 i 的前缀。
给定 T。q 次询问,求 \(f(T[l,r])\)。
预处理出 串每个后缀与 串的 LCP,这部分可以用拓展KMP(Z函数) 处理。 对于每个询问 [L,R],可以在 中找到最靠左的 mid 满足 mid-1+LCP(T[mid,n],S)>R。这一步存在多种 O(logn) 方法查找。 对于 [mid,R] 的这部分,和 S 的某个前缀一致,预处理直接算出即可。 对于 [L,mid-1] 的这部分,因为每个S 位置和 的LCP都不会超出 R,所以每 个位置答案都是 LCP 长度的前缀代价和,也可以预处理后 O(1)算出。 综上,复杂度O(nlogn) 。 设置题目时放过了根号的莫队做法,如果是\(\mathcal{O}(n\sqrt{n\log n})\) 的做法可能会 有一点卡常。