字符串大乱炖
本文是 cmd 的《字符串选讲》的速通版。
最小后缀
记号
- \(suf(s)\) 是字符串 \(s\) 的后缀集合。
- \(minsuf(s)\) 是字符串 \(s\) 的最小后缀。
- \(Ssuf(s)\) 是字符串 \(s\) 的未来可能最小后缀,对于 \(t\in Ssuf(s)\),存在一个字符串 \(r\),使得 \(tr=minsuf(sr)\)。
性质
性质 \(1\): 对于 \(u,v\in Ssuf(s)\),若 \(|u| < |v|\),那么 \(u\) 是 \(v\) 的前缀。
很显然吧,反证法很容易证。
性质 \(2\):对于 \(u,v\in Ssuf(s)\),若 \(|u|<|v|\),那么 \(2|u| \le |v|\)。
考虑反证法,假设 \(2|u| > |v|\)。
由于性质 \(1\),那么 \(u\) 是 \(v\) 的 Border,那么 \(v\) 的周期 \(|v| - |u|\) 小于 \(|v|\)。设 \(a\) 为 \(v\) 长度为 \(|v|-|u|\) 的前缀,\(v=aab\),\(u=ab\)。
如果 \(u=minsuf(sr)\),那么 \(ur<vr\implies abr<aabr\implies br < abr \implies br<ur\),与假设相反,所以 \(u\) 不可能是属于 \(Ssuf(s)\)。
即得证。
性质 \(3\):\(Ssuf(s)\) 的大小为 \(O(\log |s|)\)。
从性质 \(2\) 得证。
题目
P5211
每次修改用 \(O(\sqrt n)\) 更改信息,使查询字串哈希值为 \(O(1)\)。
考虑使用线段树维护 \(Ssuf(s)\)。每个节点维护当前区间的 \(Ssuf\),考虑合并子树信息,由于性质 \(2\),那么 \(ls\) 的 \(Ssuf\) 只保留一个,从小到大逐个比较即可,设目前最优的是 \(p\),要加入 \(t\):
- 若 \(pR\) 是 \(tR\) 的前缀,因为性质 \(2\),留下 \(t\)。
- 否则留下字典序更小的。
再通过类似的方法得到 \(Ssuf(LR)\)。复杂度 \(O(n\log^2 n+m\log^3+m\sqrt n)\)。
P5334
也类似不写了。
Runs
定义
三元组 \(r=(l,r,p)\) 是字符串 \(s\) 的 \(run\)。那么 \(s[l,r]\) 的最小周期是 \(p\),并且 \(r-l+1\ge 2p\),\(s_{r+1} \not =s_{r+1-p}\),\(s_{l-1} \not = s_{l-1+p}\)。
\(e_r=\frac{r-l+1}{p}\) 是该 \(run\) 的指数。
\(Runs(s)\) 是 \(s\) 的所有 \(run\) 集合。
性质
性质 \(1\):\(|Runs(s)| < |s|\) 且 \(\sum_{r\in Runs(s)} e_r \le 3n - 3\)。
proof is so hard。不写了。
性质 \(2\):两个周期为 \(p\) 的 \(run\) 的交小于 \(p\)。
很明显吧,如果交大于等于 \(p\),就可以合并成一个 \(run\)。
求 Runs
枚举 \(p\)。在字符串中每 \(p\) 个点中放一个关键点。那么一个 \(run\) 合法一定要经过两个相邻的关键点,枚举每个相邻的关键点用后缀排序得出它们的 \(LCP/LCS\),如果覆盖可以连接那么构成一个 \(run\)。由于性质 \(2\),得出一个 \(run\) 之后可以跳过一些关键点不访问。
当然也是会算重,比如 \(r=(l,r,p)\),可能会多算 \(r=(l,r,kp)\),后面取最小的即可,复杂度 \(O(n\log n)\)。
本原平方串
定义
一个字符串 \(s\) 是本原平方串当且仅当它的最小周期为 \(\frac{|s|}{2}\)。
性质
性质 \(1\):若 \(ss\) 是 \(tt\) 的前缀,且 \(2|s| > |t|\),可知 \(|t|-|s|\) 是 \(s\) 的周期。
画个图就知道了。
性质 \(2\):若 \(uu\) 是 \(vv\) 的前缀,\(vv\) 是 \(ww\) 的前缀,且 \(uu\) 是本原平方串,那么 \(|u|+|v|\le |w|\)。
proof is so long。不写了。
性质 \(3\):字符串 \(s\) 的本原平方串的数量为 \(O(|s|\log |s|)\)。
由性质 \(2\) 可得,因为以每个点为开始的本原平方串最多有 \(O(\log |s|)\) 个,类似斐波那契数列。
性质 \(4\):字符串 \(s\) 中本质不同的本原平方串个数为 \(O(|s|)\) 个。
考虑性质 \(2\) 中的 \(uu,vv,ww\),其中 \(2|uu| \le |w|\),那么 \(uu\) 会被计算多次,所以暂不统计,即每个点开始的只会贡献 \(2\) 个。
求出本原平方串
先求出 Runs,很明显一个本原平方串一定在一个和其周期相等的 run 中。一个 run 中所有长度为 \(2p\) 的子串都是本原平方串,由性质 \(2\) 可知,\(\sum_{r\in Runs(s)} r-l+2-2p\) 为 \(O(|s|\log |s|)\)。
题目
uoj429
考虑 \(O(n^2)\) 的 DP,发现它是根据 run 转移的,并且一个 run 的转移代表一个等差数列,一个等差数列直接继承之前即可,转移次数 \(\sum_{r\in Runs(s)} r-l+2-2p\),即复杂度为 \(O(n\log n)\)。
P6629
直接刻画所有本原平方串,去重直接差分即可。