string
题单:string
1.字符串
首先这道题中存在比较字典序的环节,这个要反应过来想到后缀排序中你可以得到任意后缀的排名。
一个经典套路是把正串和反串放在一起后缀排序。
之后求出排名后直接比较两个位置的排名就几乎已经是对的了,考虑在什么时候会错掉,发现只有在回文串的情况下会错误,需要判掉这种情况。
你直接做的话要限制 \(LCP\) 的长度小于一个不断变化的值,这个不好做。
考虑减掉回文串的贡献即可,用 manacher 跑出回文串,之后二维数点就可以。
2.Alphabetic Tree
题目要求的是树上一段路径构成的字符串在一个区间的串中的出现次数。
我们考虑对于所有串拼起来中间插入特殊字符,对其后缀排序。
那么我们要求的东西就是所有后缀与这个串 \(S\) 的 LCP 等于 \(|S|\) 的后缀个数。
显然这个东西在后缀排序后是一段排名区间。
容易得到的是,这个区间的开头一定是第一个字典序大于等于这个串的。
我们可以二分找到这个位置,二分内部可以考虑倍增,向上是简单的。向下的话,我们仿照向上的办法,从上往下跳即可。
再二分找到后缀排名的右端点。
之后就是二维数点问题。
3.Substrings in a String
数据比较小,直接考虑最原始的比较方式,暴力维护所有已经匹配的开头集合,每次往后移动时用 bitset 代替匹配,复杂度 \(O(n \sum |T|)\),对于单串的匹配是 $\frac{nm}{w} $,查一个区间只需要固定开头区间即可。
4.制胡窜
....
5.拜神
答案显然可以二分,我们只需要维护对于 \(LCP(i,j) \geq len\) 的相邻的两个排名的位置就行了,我们显然可以给每个位置记录一个后继,之后在一段区间中的最小后继即可。
现在问题变成了我们需要找到 \(LCP \geq x\) 且在一段区间中的最小后继。
相当于是你维护若干集合,集合中的任意两点的 \(LCP \geq x\),之后需要合并一个新的集合进来,我们就需要修改一下后继,启发式合并即可。
由于每次要到一个二分的区间直接查,我们要么整体二分要么就主席树,一个是时间 \(O(n \log^3 n)\),空间 \(O(n)\),另一个是时间 \(O(n \log^2 n)\),空间 \(O(n \log ^2 n)\)。
6.PalindromePalindrome
首先用 \(PAM\) 可以转化成一个树上问题。
从 \(i=1\) 到 \(n\) 操作,每次操作给定一个点,把到根链上的所有点的点权变为原来的颜色,再给这些点涂上新的颜色 \(i\),求 \(a_x-len_x+1 \geq l\) 的点中最大的 \(len_x\),对于任意点 \(x\) 有 \(len_x \gt len_{fa_x}\)。
这个操作过于复杂根本做不了,考虑继续转化。
对于每一个点 \(x\) 有若干个贡献位置 \(p_{x,1,...,m}\)。当到达一个 \(p_{x,i}\) \((i \gt 1)\) 时,把 \(a_x\) 变成 \(p_{i-1}\),求 \(a_x-len_x+1 \geq l\) 的点中最大的 \(len_x\)。
记 \(S_x\) 表示 \(n\) 次操作,操作点在树上这个位置的点的集合。
贡献位置集合 \(p_x\) 为 $\bigcup_{u\in subtree(x)}S_{u} $。
两者显然是等价的。
我们注意到对于 \(p_{x,i} 和 p_{x,i+1}\) 这两个贡献点都在一个儿子 \(son_x\) 中时,这个点对一定会被儿子中的点操作,所以我们只需要关 \(lca(u,v) = x\) 的点的,经典的可以树上启发式合并处理一下所有有用的点对,只有 \(O(n \log n)\)。
那在之后问题变成两个操作。
- 给定点 \(u\),权值 \(w\),对于从 \(u\) 到根链上所有点做操作 \(a_x=\max(a_x,w)\)。
- 对于所有点查询 \(len_x-a_x+1 \geq l\) 的点中最大的 \(len_x\)。
考虑对于 \(len\) 分块,处理出每个块中的 \(\max len_x-a_x+1\) 之后再在一个块中求单点。
对于查单点是容易的,我们对于每条重链分块,之后对于块中的点打标记然后再查询就可以了,每次往上跳复杂度是 \(O(\sqrt n)\) 的。
考虑如何查一个整块的最大值,对于每条链来说,因为有 \(len_x \gt len_{fa_x}\),那么就有每个块在深度上一定是一个区间。
我们可以预处理一个点往上跳不跳出所在 \(len_x\) 的块的最上面的点,那每次我们只需要一直往上跳即可,由于只有 \(\sqrt n\) 个块,所以只会跳 \(\sqrt n\) 次。
合起来复杂度就是 \(O(n \sqrt n \log n + q \sqrt n)\)。
7.Cat Cut
首先题目容易转化成:
对于选择的串中,除了第一个串外,每个串可以选择一个前缀,第一个串可以选择一个区间,顺次拼在一起,得到的最小字典序。
第一个选择串有点特殊我们先不管它,之后是容易用 SA + hash 处理的。
那我们现在要解决的就是。对于所有后缀,有若干串,每个串选择一个前缀,顺次拼接得到一个新串,最小化字典序。
首先可以容易的得到一个 \(O(n^4)\) 的 DP,即设 \(f_{i,j}\) 表示后 \(i\) 个串选择了 \(j\) 个位置,得到的字典序最小的串。考虑找一下这个 DP 的性质,如果打了个表可以发现, 对于任意 \((i,j)\) 有 \(f_{i,j+1}\) 为 \(f_{i,j}\) 再加上一个字符。
不过我们可以简单证明一下这个东西。
- 对于 \(1 \lt j \leq m\) 来说,一定不存在 \(f_{i,j}\) 的字典序小于 \(f_{i,j-1}\),否则一定可以把 \(f_{i,j-1}\) 调整得更优。
- 对于 \(1 \leq j \lt m\) 来说,不存在 \(f_{i,j}\) 的前 \(j\) 位优于 \(f_{i,j+1}\) 的前 \(j\) 位,否则同样可以调整。
有了上述说明,我们知道 \(f_{i,j+1}\) 只能是 \(f_{i,j}\) 增加一个串得来。
我们可以改变以下 DP,变为后 \(i\) 个串生成的字典序最小串为 \(f_i\)。
之后我们需要做的是一个串和另一个串合并找到最优分界点,我们直接暴力枚举所有分界点再用 hash 判断一下即可。
之后我们与前面合并也不难,我们对原串做后缀排序,就可以得到长度为 \(x\) 的字典序最小子串,之后也是 hash 判断即可,复杂度 \(O(n^2 \log n)\)。
8.Cool Slogans
首先转化一下题意变成初始时 \(l=1,r=n\),每次选择一个子区间,跳到 \(border\) 的位置,问最大跳跃次数。
首先有个简单的区间 dp 做法,但是状态爆了。
我们现在想要一个状态是线性的做法,设使用后 \(i\) 个位置且一定时使用 \(i\) 位置,得到的最大跳跃次数。
但是现在出现了问题,转移需要的条件是 \(LCP(i,j) \geq len_j\),\(len_j\) 表示从 \(j\) 开始的区间的长度,所以我们需要记下长度。
但是可能出现一个问题,就是可能你后面使用了很多的代价去得到一个跳跃次数较多的答案,这会导致你之后的转移出现问题,因为你可能同样以 \(j\) 为一个缩短的端点但是你之前不使用那么多的代价,导致你以后得到更高的贡献。
所以这样设计状态看上去就出现了后效性,但是我们注意到题目存在一个关键的性质。
所有更新都存在备份,更新导致的后效性会有人替你买单。
对于 \(f_i\) 来说,它是由 \(f_j\) 转移过来的,要求是 \(S [i,i+len_j-1] = S[j,j+len_j-1]\),那么对于一种 \((f_i,len_i)\) 舍弃掉一次跳跃得到的最优版本实际上就是 \((f_j,len_j)\)。
那么对于 \(f_i\) 的值肯定就不存在问题了,但是考虑 \(len_i\) 的更新应该为 $len_i = j+len_j - i $,但是此时就会出现问题,因为备份所在位置可能离 \(i\) 很远,这导致他需要多花费一些代价去继承备份,但是这是不难解决的,对于一个备份 \(j\) 来说,只要有 \(S[k,k+len_j-1] = S[j,j+len_j - 1]\),那么就可以把备份继承过去。
于是我们可以先算值,再算满足最优时 \(len_j\) 的值,之后就只需要找到距离 \(i\) 最近的满足上述继承条件的位置进行转移即可。
有了上述思路,之后就是一些 SA 的基本应用,复杂度 \(O(n \log n)\)。
9.Pumping Lemma
....
10.Naive String Splits
先对于一个位置考虑。
令 \(|S| \leq |T|\)。
如果 \(S\) 不为 \(T\) 的前缀,直接填即可。
否则,\(T = S^k +p (k \gt 0)\)。
我们可以给出一种策略,之后说明这是正确的。
每次能放 \(S\) 就放 \(S\),否则就反悔 \(k/k+1\) 个放的 \(S\),用来放 \(T\) 串。
首先如果 \(p\) 不为 \(S\) 前缀时,这个做法是正确的,因为每次放的与反悔的都是固定的,若连续放了 \(k+1\) 个 \(S\) 串,那么也无法用 \(T\) 串来替代,不能放 \(S\) 时,只有反悔 \(k\) 步最可能存在解。
先再考虑 \(p\) 为 \(S\) 的前缀的情况,首先我们可以分两种情况讨论。
- \(S\) 与 \(T\) 的最短循环节相同。
此时枚举一下各有多少个,暴力算一下就可以了。
- \(S\) 与 \(T\) 的最短循环节不同。
我们先说明先放 \(S\) 不行再放 \(T\) 的正确性。
若存在一种情况,使得这样放不优则有问题,那么此时一定有某个区间形如
\(T+S+...=S^{k+2}+...\)。
这里 \(S\) 后面有 \(...\) 是后面的串前缀一定是这个。
令 \(p+q = S\)。
那么有 \(S^k + p + p + q +...= s^k + p+q+p\)。
此时我们导出了 \(p+q = q+p\)。
令 \(|p| \leq |q|\)
那么 \(q=p+t\)。
\(p+p+t = p+t+p\),此时有 \(p+t=t+p\),之后 \(|q|\) 变为 \(|q|-|p|\),之后继续去做,即可得到 \(q,p\) 存在公共的循环节。
那么有 \(p = r^n,q=r^m\)。
那么 \(T=r^{k(n+m) +n}\),\(S=r^{n+m}\),此时 \(r\) 即为他们的公共循环节,与上文矛盾。
反悔证明类似,那对于每个位置我们这么做复杂度就是 \(O(\frac{n}{i} + \frac{\max(i,n-i)}{\min(i,n-i)})\),求和就是自然对数。
11.最后的台词
...
12.日记
对于本值不同串考虑怎么处理,直接钦定左侧串长更长,那么要求就是左侧不能再拓展了,即为 \(s_{i+1} \neq s_j\)。
那么拼出来的串包含 \(T\),要么左右本来就有 \(T\),要么中间拼接部分有 \(T\)。
把前一种判掉,那么此时考虑枚举左边匹配了前 \(x\) 个再算一下右边是否可以匹配后 \(m-x\) 个,单串的前缀与另一个串的匹配考虑直接跑 \(kmp\),建出 \(fail\) 树。
那么暴力的做其实就是对于第一棵树上到根链的所有编号 \(x\),到第二棵树中 \(m-x\) 编号的点的子树并,并且满足 \(s_{i+1} \neq s_j\)。
\(min-cnt\) 线段树,维护所有到根链中的数在第二棵树中的子树并的补集,把覆盖直接变为加就可以支持删除了,复杂度是 \(O(n \sum \log n )\),字符集是要容斥一下来的。
考虑挖掘一下题目性质,设左侧选了前 \(i\) 个,匹配了 \(x\) 个,那么有 \(s_i = T_x\) 且 \(T_{x+1}=s_j \neq s_{i+1}\),那么就有 \(T_x=s_i\) 且 $ T_{x+1} \neq s_{i+1}$,想一下这意味着什么。
这相当于是 \(LCP(s[i-x+1,n],T[1,m]) = x\),这个就是对于每个后缀和串 \(T\) 的 \(LCP\),这个直接做 \(Z\) 函数即可找到,那么现在只有 \(n\) 个有用的位置了。
要计算并集的话,我们考虑在第二棵树上从根链往下走,之后打标记,算一下从根链往下有多少个不同的左侧匹配数 \(x\) 即可,复杂度 \(O(n)\)。
13.Apparently Make UTPC
首先对于原问题进行一些分讨,对于这些情况取最优解。
首先如果确定了中间串,有贪心:
此时对于串中第一个不为 \(S_0\) 的数,若 \(S_i \gt S_0\) 最前面从小往大放 \(1\) 到 \(S_0\),否则放 \(1\) 到 \(S_0 -1\),设 \(lim\) 为这个界限。
- 两个串有包含关系
做一遍上面的贪心,最后再依次放剩下的。
- 两个串不交
做两遍上面贪心,最后放一次放剩下的。
- 有交但不包含
设交的长度为 \(p\),那么中间串为 \(S[0,n) + T[p,m)\)。
显然 \(p\) 有一个最小界限。
然后再找出可以满足相交的 \(p\) 的集合。
接下来从小往大一次 \(ck\) 所有长度。
设之前答案为 \(p\),现在 \(ck\) 长度为 \(i\)。由于中间是相交所以中间串以此遇到的字符的顺序不变,只有个数改变,所以 \(lim\) 固定。那么若 \(T[p+1,i]\) 中存在小于等于上述贪心 \(lim\) 的值,则取 \(p\),否则比较 \(T[p,m)\) 和 \(T[i,m)\) 的字典序即可。
每次 \(ck\) 复杂度 \(O(\log n)\),预处理一个区间 \(\min\) 是 \(O(n \log n)\),总复杂 \(O(n \log n)\)。
14.A task for substrings
首先显然有一个 $ O(n \sqrt {\sum |S|}) $ 做法,但是没什么用。
首先先容斥一下变成 \(Ans_{1\leq x\leq y\leq r}\) - \(Ans_{x\leq {l-1} \lt y \leq r}\)。前一个是好做的,后一个注意到有两个关键点 \({l-1,l}\),可以直接拆成一个前缀的后缀和一个后缀的前缀了,这个对于正反串建一个 \(ACAM\) 跑一个二维数点即可,复杂度 \(O(n \log n)\)。

浙公网安备 33010602011771号