字符串杂记

可能会一直更新,杂七杂八记一些东西,前后应该没有联系。

Manacher 与回文串

首先给相邻两个字符之间加入占位符 #,我们求出一个数组 \(z_i\) 代表 \([i-z_i,i+z_i]\) 为以 \(i\) 为中心的最常回文串。一个字符一个字符插入,记之前的最远的回文串中心为 \(p\),右端点为 \(r\),现在需要求 \(z_i\),如果 \(i \le r\):那么首先 \(z_i\) 的答案可以从 \(\min(z_{2p-i},r-i)\) 继承过来,然后暴力扩展,时间复杂度显然均摊 \(O(n)\)。

Z 函数

我们定义 \(z_i\) 为 \(\text{LCP}(s,s[i:])\),考虑怎么求,为了更好利用以前求过的 \(z_i\),我们维护两个指针 \([l,r]\) 代表当前最远的 \(z_l\) 匹配到了 \(r\)。对于一个 \(i\),如果满足 \(i \le r\),那么 \(s[i:]\) 和 \(s[i-l+1:]\) 肯定有一段长度为 \(\min(z_{i-l+1},r-i+1)\) 的 \(\text{LCP}\),那么继承 \(z_{i-l+1}\) 然后暴力扩展均摊就是线性的。两个串的话拼在一起,中间用 # 隔开即可。

AC 自动机

懒得写了。

UOJ656.【ULR #2】霸占排行榜

字符串太难了。首先对 Trie 建立 ACAM,把 \(T\) 放到上面去跑。肯定不能直接跑,我们考虑一个整串一起跑。考虑加入 \(S_i\),ACAM 的指针移动一定形如先走一段,再跳一次 fail,然后重复的过程。

我们不妨设 \(j\) 节点在加入 \(S_i\) 的过程中,第一次跳 fail 前的点为 \(end_{i,j}\),记跳之后的点为 \(x\),记加入最后到达的点为 \(dir_{i,j}\),由于 ACAM 的性质,\(\text{LCS}(end_{i,j},u_x)\) 肯定等于 \(u_x\)。记 \(dep_u\) 为 \(u\) 在字典树上的深度,记 \(\Delta d=dep_{end_{i,j}}-dep_j\)。首先考虑求出来 \(dir_{i,j}\),讨论:

  • \(\Delta d \ge dep_x\):那么 \(u_{dir_{i,j}}\) 一定是 \(S_i\) 的后缀,从 \(j\) 出发和从 \(1\) 出发是等价的,\(dir_{i,j}=dir_{i,1}\)。

  • 否则,那么从 \(j\) 出发和从 \(anc(x,\Delta d)\) 出发是等价的。

也就是说,从 \(j\) 出发一定会和从一个祖先出发等价,考虑做深搜,维护神搜栈来维护祖先,下面的节点可以从上面的祖先继承答案。接下来考虑怎么打 ACAM 的标记,首先 \(j \to end_{i,j}\) 一段的标记可以直接树上差分打出来,而后半部分如果从 \(1\) 出发可以直接在 \(S_i\) 的后缀下标上打差分标记,否则把标记打到 \(anc(x, \Delta d)\) 上,从 fail 树下面往上面做就好。

接下来考虑 \(end_{i,j}\) 的求法,如果字典树是一条链 \(H\) 的话,要解决的就是 \(S_i\) 和 \(H\) 某个后缀的 LCP,Z 函数可以解决。考虑对字典树做重链剖分,首先对重链做 Z 函数,现在相当于有 \(O(n\ell)\) 个形如 \((a,b)\),代表在轻儿子 \(a\),查询后缀 \(b\) 的最长匹配的问题。离线后对于一个单独的 \(a\),我们把所有 \(b\) 跑后缀排序,按照字典序单增来算答案。

我们惊讶地发现,后缀排序后的结构其实是和字典树上 LCP 是很匹配的,后缀排序相邻两个串之间发生的变化是不停踢掉最后一个字符直到是下一个串的前缀,然后再在后面加入若干个串,放到字典树上面就是从一个点向上一直跳直到成为 fail 树上的祖先,然后再往下走。由于轻儿子子树和大小是 \(O(\ell \log \ell)\) 的,所以就做到了这个复杂度。由于出边的字符是单调的,所以可以用指针代替可持久化数组。总时间复杂度 \(O(\ell \log \ell + n\ell + S + m)\)。

P14493 [集训队互测 2018] Fim4

首先我们考虑把文本串放到字符串们构成的 ACAM 上面去跑。根号分治,小的部分我们可以 \(O(nB)\) 快速跑,大的部分最多只有 \(O(\frac{m}{B})\) 个,我们把这些大的一起处理掉,记为 \(S_i\)。下文的字典树是 \(S_i\) 组成的字典树。下面的 \(\ell = \sum |qs_i|,m=\sum |s_i|\)。然后套用 UOJ656 的做法。

但是实际上,本题的字典树是由若干字符串组成的,实际上完全可以更简化,把所有 \(qs_i\) 拼到一起中间用特殊符号隔开跑 Z 函数就是对的,认为 \(m,\ell\) 同阶,所以大的部分的复杂度是 \(O(\frac{\ell^2}{B})\)。两部分平衡可以得到复杂度 \(O(\ell \sqrt n)\)。

Border 理论

弱周期引理:若字符串 \(S\) 有 \(p,q\) 周期且满足 \(p+q \le |S|\),那么 \(S\) 有 \(\gcd(p,q)\) 的周期。

设 \(p>q\),考虑证明 \(p-q\) 也是周期。如果 \(i \le q\),则有 \(s_{i} = s_{i+p} = s_{i+p-q}\),否则有 \(s_i = s_{i-q}=s_{i+p-q}\)。

Theorem 1. 若串 \(u,v\) 满足 \(2|u| \ge |v|\),则 \(u\) 在 \(v\) 中的匹配位置形成等差数列。若它的项数不少于 \(3\),则其公差为 \(u\) 的最小周期。

考虑找到 \(u\) 的第一次,第二次,和任意一次的匹配的位置,记第一次第二次间距为 \(d\),第二次和后面一次间距为 \(q\),那么由于弱周期引理,\(r= \gcd(d,q)\) 也是 \(u\) 的周期。记最小正周期为 \(p \le r\),那么有 \(p\ |\ r\ |\ d\)。如果 \(p<d\),那么把第一次匹配往右移动又会形成一次匹配,所以 \(p=d\),那么也就是说 \(d \ | \ q\),那么就证完了。

Theorem 2. \(S\) 长度不小于 \(|S|/2\) 的 border 长度构成一个等差数列。

设长度最大的 border 长度为 \(p\),另外一个长度为 \(q\),那么 \(S\) 存在 \(n-p,n-q\) 的周期,那么由于弱周期引理,也就存在 \(\gcd(n-p,n-q)\) 的周期。由于 \(p\) 最大,所以需要有 \(\gcd(n-p,n-q) \ge n -p\),那么也就是说 \(n-p \ | \ n-q\)。那么所有可能的 \(n-q\) 构成等差数列,而所有可能的 \(q\) 也构成等差数列。

Theorem 3. \(S\) 的所有 border 长度构成 \(O(\log |S|)\) 段等差数列。

首先把长度过半的 border 取出作为等差数列,然后取出长度最小的 border \(T\)。说明 \(|T| \le \frac{3}{4} |S|\)。

  • 如果最小循环节 \(d \le |S|/4\),不断从 \(T\) 中减去 \(d\) 则有 \(|T| \le \frac{3}{4}|S|\)。

  • 如果最小循环届 \(d > |S|/4\),显然成立。

然后把 \(T\) 作为新的 \(S\),那么可以得到 \(O(\log |S|)\) 段等差数列。

P4156 [WC2016] 论战捆竹竿

考虑到 border 只有 \(O(\log n)\) 段等差数列,我们找出来然后跑同余最短路即可,时间复杂度 $O(n)

Significant Suffixes

我们记 \(\text{suf}(S)\) 代表 \(S\) 的所有后缀,记 \(\text{ssuf}(S)\) 代表可能成为 \(S\) 最小后缀的集合。

Lemma. 1:对于 \(u,v \in \text{ssuf}(S) \wedge |u| < |v|\),那么 \(u\) 是 \(v\) 的 border。

因为 \(u,v\) 都是 \(S\) 的后缀,那么 \(u\) 肯定是 \(v\) 的后缀,如果 \(u\) 不是 \(v\) 的前缀,那么 \(ut\) 和 \(vt\) 的大小关系不变,矛盾。

Lemma. 2:对于 \(u,v \in \text{ssuf}(S) \wedge |u| < |v|\),那么 \(2|u| < |v|\)。

考虑反证,如果 \(|u| < |v| \le 2|u|\),并且 \(u\) 是 \(v\) 的 border,那么有一个 \(|v|-|u|\) 的周期 \(a\),记 \(u=ab\),\(v=aab\),显然 \(u\) 不优于 \(b\) 和 \(aab\),所以矛盾。这个性质推出来 \(|\text{ssuf}(S)| = O(\log |S|)\)。

P5334 [JSOI2019] 节日庆典

我们首先维护出来 \(\text{ssuf}(S[1:i])\),首先考虑这个怎么维护。我们对于两个在 \(\text{ssuf}(S[1:i-1])\) 的候选后缀 \(u,v(|u|<|v|)\),首先我们需要 \(u\) 是 \(v\) 的前缀,否则踢掉大的那一个。然后需要满足 \(2|u| < |v|\),否则把 \(u\) 踢掉。

那么剩下的工作我们需要比较两个候选项 \(a,b\) 哪个更优,注意到这其实就是在比较 \(S[a:i]+S[1:a)\) 和 \(S[b:i] + S[1:b)\) 的字典序大小,不妨设 \(a<b\),那么 \(S[b:i]\) 一定是 \(S[a:i]\) 的前缀,剩下的部分 Z 函数可以解决。时间复杂度 \(O(n \log n)\)。

杂项

CF1909G Pumping Lemma

由于 \((k-1)|y| = n-m\),所以可以求出来 \(|y|\) 的所有可能值,共 \(O(\sqrt n)\) 个。

枚举 \(xy\) 和 \(z\) 的分界点,如果前后缀都匹配的话,剩下的区间就是 \(y^{k-1}\),考虑求它的最小正周期,其余的周期都是最小正周期的整数倍。也就是需要求出来每个长度为 \(m-n\) 的区间的最小正周期。

由于最小周期的长度 \(\le \text{LCS}(xy,y^{k-1})\),讨论滑动窗口由 \([i,i+(m-n)] \to [i+1,i+1+(m-n)]\) 的过程:

  • 如果 \(s_{i} \ne s_{i+1+(m-n)}\):那么 \(\text{LCS}=0\)。
  • 如果 \(s_i = s_{i+1+(m-n)}\):那么之前 \(i\) 位置的所有周期在 \(i+1\) 的位置都成立,由于 \(\text{LCS}\) 的长度增加了 \(1\),只需要判断这个新的周期即可。

那么问题变为给定 \(a,b\),有多少个 \(x\) 满足 \((a \ | \ x) \wedge (x \ | \ m-n) \wedge (x \le b)\),把 \(b\) 离线之后从小往大扫,每扫到一个 \(m-n\) 的因数就给它的因数打标记即可,时间复杂度 \(O(n)\)。

posted @ 2026-07-18 15:31  nullpt3  阅读(21)  评论(0)    收藏  举报