集训内容总结 day3:字符串
其实是数据结构。
SAM 入门推荐读物:https://www.cnblogs.com/zaza-zt/p/15419181.html
AC 自动机
GYM103409H
\(S[0,2^k-1]=S[0,2^{k-1}-1]+S[0,2^{k-1}-1]^{-1}\)
可以把一个大区间拆成个若干长度为二整数次幂的小区间。这样有 \(O(n\log n)\) 个完整区间。不同的区间只有 \(\log\) 个。
在 AC 自动机上一段段跑,\(d_{u,k,1/-1}\) 表示 \(u\) 走 \(S[0,2^k-1]^{1/-1}\) 到了哪里。然后还要记每个点过了多少次,记 \(f_{u,k,1/-1}\) 表示这段共走了多少次,直接按 \(k\) 往下传即可。
P5310
考虑 \(b_i=\sum_{j<i}[a_j>a_i]\) 来刻画这个关系。
建 AC 自动机,跳 fail 时只需 check 一段区间内有多少个数比 \(x\) 大就能判断这个 fail 是否可以成为后缀。
CF1801G
考虑 ACAM 先前缀和一下右节点小于某个值的匹配。这样只有 \(l<ql\le r\) 的点被额外算了。那么我们就需要找出有多少对 \([l,ql-1]\) 和 \([ql,r]\) 匹配。
\([l,ql-1]\) 看作 \([1,ql-1]\) 不断跳 fail,\([ql,r]\) 先建出反串的 ACAM 然后从 \([ql,qr]\) 对应节点跳 fail。找 \([ql,qr]\) 可以倍增。
然后变成找两棵树上匹配的点对,可以直接做。
QOJ7742
SA 和 SAM
QOJ5312 改
\(O(n^2)\) 和 \(O(nk)\) 直接暴力 dp 即可。
将 dp 丢到二维平面上,发现一条 \(f_{x+i,y+i}\) 关于 \(i\) 是单调的,而总共有 \(k\) 条这样的斜线。记 \(g_{i,j}\) 表示第 \(i\) 条斜线使得 \(f\) 值为 \(j\) 的第一个位置。这样状态就是 \(O(k^2)\) 了。
如何转移?发现考虑当前是什么操作,然后操作后序列需要查 \(s,t\) 的某个后缀的最长公共前缀即可。复杂度 \(O(k^2+n\log n)\)。
UOJ395
P4482
等价于求 \(LCS([1,i],[1,r])\ge i-l+1\) 的 \(i\)。考虑变成在 SAM 上求 \(a_i,a_r\) 的 \(len(lca(a_i,a_r))\)。
直接跳父亲显然倒闭,可以考虑树剖。分成两类贡献:轻子树和链上的点和这条链末端的子树。原因是轻子树的大小之和为 \(O(n\log n)\)。对于子树的部分直接线段树合并。
CF1098F
QOJ11105
P6292
对 \(r\) 扫描线,维护所有 \(ans(l,r)\) 的答案。从 \(r-1\to r\),每次只有长度大于某个值的点才会新被加入。
对于某个等价类找出最近的 endpos,贡献就是区间加等差数列。
设法把所有最近 endpos 相同的等价类一起做贡献。每个点都记录最近的 endpos,相同的看成一个重链。相当于找所有到根路径上的重链贡献答案,然后 access。用 LCT 维护即可。
CF104491E
QOJ6507
先跑 toposort 找出每个点代表区间长度。记 \(a(l,r)\) 表示 \(l,r\) 指向哪个点。找出 \(a(l,r)\) 可以分析 \(a(l-1,r),a(l,r+1)\) 得出。这样是 \(O(|ans|^2)\) 的。
优化不会了。