2024.4.20 - 4.24
Sat
ABC350
A. 简单模拟,使用 "ABC%d" 与 scanf
读入非常简单。
B. 简单模拟。
C. 模拟,非常绕的置换环,这使我大脑旋转。
D. 并查集记录各连通块大小。
E. 期望 DP + 记忆化。
F. 根据括号嵌套关系建树即可。
G. 启发式合并。
Sun
Legen...
【CF696D】
给定 \(n\) 个模式串 \(S_i\),各模式串有权值 \(a_i\),你需要构造一个长度为 \(L\) 的文本串,文本串中每出现一个模式串则增加 \(a_i\) 的权值,最大化该文本串的权值。
\(n, \sum |S_i|\leq 200, a_i\leq 100, L\leq 10^{14}\)。
考虑 DP,记 \(g_i\) 表示转移到 \(i\) 节点增加的权值总和,若 \(f_{i,j}\) 表示长度为 \(i\) 转移到节点 \(j\) 的最大权值,后继状态为 \(to\),则转移为:
各维度独立,考虑矩阵快速幂优化,将可行的节点间转移计入矩阵,广义矩阵乘法运算为 \((\max, +)\),时间复杂度为 \(\mathcal{O}(M^3)\),其中 \(M\) 为 AC 自动机节点数。
The ABCD Murderer
【CERC2018】
给定 \(n\) 个报纸单词,你需要制作剪报勒索信,即字符串前后拼接且可以在相同内容时重叠,你预计的勒索信的内容为 \(T\),请问最少剪出的单词数量。
\(n, \sum |S_i|, |T|\leq 3\times 10^5\)。
考虑 DP,记 \(f_i\) 表示考虑到第 \(i\) 个字符,若 \(T_{[j+1,i]}\) 是模式串,则 \(f_i \gets \min(f_i, f_j + 1)\).
对于一个字符而言,我们用一个更长的串覆盖显然不劣,使用 AC 自动机求出以 \(T_i\) 结尾的最长单词的长度 \(L_i\),随后有 \(f_i = \underset{i- L_i\leq j\leq i-1}{\min} f_j + 1\)。
线段树优化即可。
You Are Given Some Strings...
【CF1202E】
给定一个文本串 \(T\) 与 \(n\) 个模式串 \(S_i\),对所有满足 \(1\leq i,j\leq n\) 的二元组 \((i,j)\) 求 \(S_i + S_j\) 在 \(T\) 中的出现次数。
\(n, \sum |S_i|, |T|\leq 2\times 10^5\)。
考虑枚举划分点 \(p\),考虑 \(T\) 以位置 \(p\) 结尾的前缀处恰好是 \(f_p\) 个模式串的结尾,考虑 \(T\) 以位置 \(p\) 开头的后缀处恰好是 \(g_p\) 个模式串的开头。
开两个 AC 自动机,分别存储正向模式串和反向模式串,求出 \(\{f\}, \{g\}\) 后,\(\sum f_i g_{i+1}\) 即为答案。
e-Government
【CF163E】
给定一个 \(n\) 个模式串 \(S_i\),记 \(X\) 为当前的字符串集合,初始时所有模式串都属于集合,\(q\) 次操作,每一次操作要么从 \(X\) 中加入或删除一个模式串,要么给出文本串 \(T\) 询问匹配次数的总和。
\(n,q\leq 10^5, \sum |S_i|, \sum |T|\leq 10^6\)。
考虑建出 fail 树后每个节点都会影响一棵子树,加入或者删除相当于子树加一或减一,询问的根链查询相当于单点查询,使用树状数组即可。
魔法咒语
【BJOI2017】
给定 \(n\) 个拼接串 \(S_i\) 和 \(m\) 个模式串 \(T_i\),对于一个文本串 \(X\),如果它是好的仅当有一种划分使得其每一段都是一个拼接串且不存在一个子串是模式串,求长度为 \(L\) 的好的 \(X\) 的数量。
\(n,m\leq 50, \sum |S_i|, \sum |T_i|\leq 100, L\leq 10^8\)。
Data Type I: \(L\leq 100\)。
Data Type II: \(|S_i|\leq 2\)。
为模式串建立 AC 自动机,处理出 \(trans_{i,j}\) 表示节点 \(i\) 通过拼接串 \(S_j\) 到达的节点。
转移 \(i\to trans_{i,j}\) 合法仅当便利过程中不存在一个节点 \(u\) 使得根到 \(u\) 上的某一个节点是模式串的结尾。
对于一类数据:
记 \(f_{i,j}\) 表示长度为 \(i\),当前到达 AC 自动机上节点 \(j\) 的方案数,则:
初始有 \(f_{0,0} = 1\)。
对于二类数据:
考虑到 \(|S_i|\leq 2\),也就是说 \(f_i \to f_{i+1}\) 或 \(f_i\to f_{i+2}\),仿佛可以矩阵乘法,但是 \(i+1\) 与 \(i+2\) 如何处理?
我们可以 \([f_{i-1}, f_i]\times M = [f_i, f_{i+1}]\),其中 \(f_i\) 是一个长度为 AC 自动机节点数量的行向量,\(M\) 是一个 \(2\times 2\) 的元素为矩阵的矩阵,可以分成四个行列均为 AC 自动机节点数量的矩阵。
于是,左上部分全零,左下部分为单位矩阵,右上部分为所有长度为 \(2\) 的转移,右下部分为所有长度为 \(1\) 的转移,矩阵快速幂即可。
阿狸的打字机
【NOI2011】
一个打字机,支持键入小写字母,退格,在新一行打印当前文本,记行数为 \(n\),多次询问第 \(x\) 行的字符串在第 \(y\) 行的字符串中出现了多少次?
\(n,m\leq 10^5, |S|\leq 10^5\)。
该方式方便构建 AC 自动机的字典树部分,需要记录父亲节点,首先考虑询问怎么求:考虑 fail 指针的性质,则从 \(y\) 的结束点开始在字典树上跳父亲中的所有节点中,有多少个节点可以通过跳 fail 达到 \(x\) 的结束点。
直接暴力,或者将询问按照 \(y\) 分类。
在该自动机上,有 Trie 树与 fail 树,接下来注意区分。
考虑优化:如果预先标记 \(y\) 及其所有父亲,每一个 \(x\) 都是子树和的询问,【单点加,子树求和】,这启发我们使用树状数组。
但是每一个 \(y\) 都暴力跳父亲太慢了,我们直接在 Trie 上深搜,进入时加一,出去时减一,每到达一个节点遍历在该节点结尾的字符串编号,此时可以处理关于这个字符串为 \(y\) 的询问,区间求和即可回答每一组询问。
L 语言
【HNOI2004】
给定 \(n\) 个模式串 \(S_i\) 与 \(m\) 个文本串 \(T_i\),对每个文本串求其最长的前缀,满足该前缀存在一种划分使得每一段都是模式串。
\(n\leq 20, |S_i|\leq 20, m\leq 50, |T_i|\leq 2\times 10^6\)。
记词典为 \(D\),考虑 DP,则有:
考虑到 \(|S_i|\leq 20\),我们需要记录前 \(20\) 个 \(f\) 的值,为了方便,记一变量 \(x\) 满足 \(x\) 的第 \(j\) 位(位编号 \(1\sim 20\))是 \(f_{i-j}\) 的值,同时记录 fail 树上根到该节点所有可能的结尾长度 \(g_t\)。
考虑一组询问,每一次询问就相当于到达某个节点 \(u\),然后判一下 \(g_u\) 与 \(x\) 是否有交,然后对于 \(x\) 做一些运算,使用位运算就非常方便,个人写法:
int x=2
bool res=(x&acam[now].num);
x=(((x<<1)|(res<<1))&0x1fffff);
考虑到 \(f_0 = 1\) 所以 \(x\) 赋值为 \(2\)。
Mon
Death DBMS
【CF1437G】
给定 \(n\) 个模式串 \(S_i\),可能有重,\(m\) 次操作,要么修改某个模式串的权值,要么查询是给定文本串的子串的模式串的权值最大值。
\(n,m, \sum |S_i|, \sum |T_i|\leq 3\times 10^5\)。
问题等价于建出 fail 树后的单点修改与链查询问题,树剖维护即可。
Mike and Friends
【CF547E】
给定 \(n\) 个模式串 \(S_i\),记 \(f(i,j)\) 表示 \(S_i\) 在 \(S_j\) 中的出现次数,多次询问 \(\overset{r}{\underset{i=l}{\sum}} f(k,i)\) 的结果。
\(n, \sum |S_i|\leq 2\times 10^5, m\leq 5\times 10^5\)。
根据【阿狸的打字机】的相关做法,差分询问,变成 \(S_k\) 在一段前缀内的出现次数,按照顺序遍历每一个 \(S_i\) 并将其在字典树上的节点 \(+1\),询问就是子树求和,树状数组维护即可。
Banned Substrings
【ABC305G】
给定 \(m\) 个模式串 \(S_i\),求有多少个长度为 \(n\) 的文本串不包含任意模式串作为子串?字符集为 ab
。
\(m\leq 126, |S_i|\leq 6, n\leq 10^{18}, |\Sigma|=2\)。
首先搞出转移矩阵,只需要判断一下目标转移是否合法,然后矩阵快速幂即可,时间复杂度为 \(\mathcal{O}(M^3)\),其中 \(M\) 是 AC 自动机上节点数量。
奥术神杖
【BJOI2019】
给定 \(n\) 个模式串 \(S_i\) 与其对应的权值 \(V_i\),求出一个长度为 \(m\) 的文本串 \(T\),某些位置固定字符,若模式串 \(S_i\) 出现了 \(x_i\) 次,并记 \(c\) 为 \(x_i\) 的总和,则文本串的权值 \(f(T) = \sqrt[c]{\overset{n}{\underset{i=1}{\prod}} V_i^{x_i}}\),构造权值最大的 \(T\)。
\(n,m, \sum |S_i|\leq 1501, 1\leq V_i\leq 10^9\),字符集为阿拉伯数字字符,故 \(|\Sigma|=10\)。
首先,这个权值的计算方式就非常离谱,考虑 \(\ln - \exp\),乘法转为加法:
且已知 \(e > 1\),故 \(a > b\) 可以等价于 \(\ln a > \ln b\)。
如果我们记所有匹配的 \(c\) 个模式串的权值为序列 \(\{W\}\),则:
那么很显然就是关于 \(\ln V_i\) 的 0/1 分数规划问题,至此我们完成第一步。
知道这个,我们就可以大力跑 DP,将模式串插入 AC 自动机,记每个节点的权值为 \(g_u\),当前节点 \(u\) 存在一个后继节点 \(to\),然后尝试用 \(f_{i,u} + g_{to}\) 更新 \(f_{i+1,to}\)。
最终的结果为 \(f_n\) 中的最大值,check 成功等价于最大值 \(> 0\),至此我们完成第二步。
如何保证精度?我们可以发现,这个权值的计算方式等价于 \(\{W\}\) 的几何平均数,那么根据定义其值在 \(V_i\) 的值域范围内。
于是我们的二分的下界为 \(0\),上界为值域的 \(\ln\),其实开到 \(30\) 就可以过,二分几十次就能出结果,至此我们完成最后一步。
如何输出方案?写过 DP 输方案的都知道记录到达该节点的转移方法。
记值域为 \(V\),AC 自动机的节点总数为 \(s\),时间复杂度为 \(\mathcal{O}(ns|\Sigma|\log_2 \ln V)\)。
Tue
后缀数组初探
文本校正
【SDOI2017】
给定两个长度为 \(n\),元素范围为 \([1,m]\) 的正整数序列 \(S\) 和 \(T\),请问是否存在一种将 \(T\) 划分成三段的方法使得:
- 各段均非空。
- 存在一种方式重排三段之间的顺序使得其与 \(S\) 一致。
\(\sum n, \sum m\leq 10^6, n\ge 3\),多测。
字符串基础算法(KMP & Z 函数 & Manacher) + 抽象大分讨。
我们记将 \(T\) 划分成的三段编号为 \(1,2,3\),则排布方案总共有 \(123,132,213,231,312,321\) 共 \(6\) 种。
情况 A:\(123\)
简单的,判断 \(S,T\) 是否一致即可,时间复杂度为 \(\mathcal{O}(n)\)。
情况 B:\(231,312\)
等价于判断 \(S,T\) 是否循环同构,倍长其中一个然后 KMP 即可,时间复杂度为 \(\mathcal{O}(n)\)。
注意如果求出其中一段的长度为 \(1\) 则不能划分该段。
情况 C:\(321\)
后缀数据结构可行,但是比较难想到的一种构造方式可以做到更加实现更加简单。
考虑倒转 \(S\) 插入 \(T\) 形成 \(T_1S_nT_2S_{n-1}\cdots T_nS_1\) 的形式,则该问题要求将前面的串划分成三个偶回文串。
枚举前缀的一个,考虑判定后缀是否为双回文串。
引理:若 \(S\) 为双回文串,则必然存在一种回文串划分 \(P + Q\) 满足 \(P\) 是 \(S\) 的最长回文前缀或 \(Q\) 是 \(S\) 的最长回文后缀。
LJQ の 证明:假设 \(P\) 是长度大于 \(S\) 的一半的回文前缀,且存在一组最长的回文前缀 \(R\) 满足 \(|R| > |P|\),对应后缀为 \(X\),那么显然 \(R\) 有回文周期,于是可以推导出 \(X\) 的后缀也为周期,但是 \(X\) 的中间部分呢?它肯定不属于周期,那么 \(Q\) 为 \(S\) 的最长回文后缀。
于是我们需要求出 \(R_i\) 表示从第 \(i\) 个字符开始的最长回文子串的结束位置,并处理出所有的回文后缀,顺序扫描然后判定,前面用 Manacher 实现,时间复杂度为 \(\mathcal{O}(n)\)。
情况 D:\(213,132\)
可以发现这两种情况对称,下面举例 \(213\),若为 \(132\) 则翻转 \(S,T\) 即可。
此时 \(T\) 的形式为 \(123\),而 \(S\) 的形式为 \(213\)。
我们记 \(Z(S)_i\) 表示 \(T + 10^9 + S\) 的 Z 函数对应值,其中第 \(n+1\) 位的 \(10^9\) 相当于字符串中的特殊字符;同理有 \(Z(T)_i\) 表示 \(S + 10^9 + T\) 的 Z 函数对应值。
假设 \(S,T\) 的 LCS 长度为 \(L\),并记 \(2\) 的长度 \(x\) 与 \(1\) 的长度 \(y\),则有解等价于:
- \(x + y\ge n - L\)
- \(Z(S)_{n+2+x}\ge y\)
- \(Z(T)_{n+2+y}\ge x\)
那此时一个树状数组就可以了,时间复杂度为 \(\mathcal{O}(n\log_2 n)\),但这不够优秀。
枚举 \(x+y\),考虑其中的 \(1\),其为 \(T_{[1,x+y]}\) 的前缀与 \(S_{[1,x+y]}\) 的后缀,考虑给 \(T\) 预处理 KMP 然后匹配 \(S\) 并在 fail 树上维护信息。
维护什么呢?给 \(T\) 预处理 KMP 时考虑维护 \(mx_i\) 表示 fail 树上根到该节点中使得 \(y + Z(T)_{n+2+y}\) 最大的 \(y\)。
然后扫描 \(S\) 的时候,假设当前位置为 \(i\),那么很显然就是条件中 \(x + y\) 的值,并且匹配到 \(T\) 的某个位置 \(j\),则 \(j\) 对应的 \(mx_j\) 就是使得 \(y + Z(T)_{n+2+y}\) 最大的 \(y\),那么有 \(\max\{y + Z(T)_{n+2+y} - (x+y)\} = \max\{Z(T)_{n+2+y} - x\}\),我们希望这个值大于等于 \(0\),也就是 \(y + Z(T)_{n+2+y}\ge i\),于是时间复杂度也是 \(\mathcal{O}(n)\) 的。
可重叠最长重复子串
给定字符串 \(S\),求出 \(S\) 中最长的出现了至少两次的子串的长度。
\(1\leq |S|\leq 2\times 10^5\)。
等价于求 \(S\) 的任意两个后缀的 LCP 的最大值,即求 \(\text{Height}_i\) 的最大值。
多串匹配
给定一个文本串 \(S\),在线地为每一个模式串 \(T_i\) 判断 \(T_i\) 是否是 \(S\) 的子串。
\(1\leq |S|, \sum |T_i|\leq 10^5\)。
子串,是后缀的前缀,二分其后缀对应的排名再 check 一遍是不是前缀即可。
Wed
Milk Patterns
【USACO06DEC Gold】
给定长度为 \(n\) 的非负整数序列 \(A\),求出 \(A\) 中最长的出现了至少 \(k\) 次的子串的长度。
\(1\leq k\leq n\leq 2\times 10^4, 0\leq A_i\leq 10^6\)。
等价于求 \(S\) 的任意 \(k\) 个后缀的 LCP 的最大值,即求连续 \(k-1\) 个 \(\text{Height}_i\) 中最小值的最大值。
若 \(k=1\) 时特判为 \(n\)。