字符串技术巡礼

· · 个人记录

字符串技术巡礼

前言

本文主要内容是一些许多人不怎么会的字符串组合算法,而主要内容基本可以理解为论文导读。笔者认为,许多技术是可以被放入(甚至可以说很适合被放入)OI 的框架中的,只是由于这些技术引入 OI 的时机较晚,因此尚未得到普及。

1. 记号与约定

字符集是一个全序集,默认是有限的,记为 \Sigma

字符串是由若干字符(字符集的元素)构成的序列,字符串的集合记为 \Sigma^*,一般用小写字母 u,v,w 等表示字符串,也有时用大写字母 S,T 表示字符串。

2. 前置知识

并非在每种技术中都会用到全部的前置知识,但这些是字符串理论的基础,几乎都在 NOI 大纲内。

字典树

不再赘述。

自动机

不再赘述。

我们通常考虑有限状态自动机,其中分为确定性有限状态自动机和非确定性有限状态自动机。字典树也可以看作一个自动机。

Border 和周期

对于字符串 w,若整数 0<p\leq |w| 满足 w_i=w_{i+p}\ (i\leq |w|-p),则称 p(或 w[1,p])是 w 的一个周期。

若整数 0\leq p<|w| 满足 w[1,p]=w[|w|-p+1,|w|],则称 p(或 w[1,p])是 w 的一个 Border。

结论:pw 的周期当且仅当 |w|-pw 的 Border。

定理(周期引理,Periodicity Lemma):若 p,q 都是 w 的周期,且 p+q\leq |w|+\gcd(p,q),则 \gcd(p,q)w 的周期。

弱周期引理(Weak Periodicity Lemma,WPL)是一个弱化版本,即将上述条件替换为 p+q\leq |w|

结论:w 的长度在 (x,2x] 中的所有 Border 构成一个等差数列。对于周期,有类似的结论。

我们可以 O(|w|) 地求出 w 的全部周期或 Border。

Aho-Corasick 自动机

对于一个字符串集合 W=\{w_1,\ldots,w_k\},令 W_p 是每个 w_i 的所有前缀构成的集合。建立 W_p 的字典树 T,字符串 u 对应的树上结点记为 f(u)

对于树上结点 x,设 u=f^{-1}(x)vu 的最长的属于 W_p 的真后缀,定义 B_x=f(v)。数组 B 称为失配数组。以 B_xx 的父结点将形成一棵树 T',称为失配树。

令:

tr(x,c)=\begin{cases}ch(x,c) & (\exists ch(x,c)) \\ tr(B_x,c) & (\text{otherwise})\end{cases}

其中 ch(x,c) 表示 T 上与 x 之间边的字符为 c 的子结点。

tr 为转移,这得到了一个自动机,称为 W 的 Aho-Corasick 自动机,简称 AC 自动机(ACAM)。

W=\{w\} 时,这种特化的单串 AC 自动机称为 KMP 自动机,失配数组的含义为 w 的每个前缀的最长 Border 位置。

结论:设串 uW 的 AC 自动机上运行到点 x,则 u 的最长的属于 W_p 的后缀为 f^{-1}(x)

结论:W_p 中所有 f^{-1}(x) 的后缀恰好是所有 f^{-1}(y)y 取遍 x 在失配树上的祖先。

我们可以 O(\sum_{w\in W}|w|\log |\Sigma|) 地建立 W 的 ACAM(事实上,是 O(\sum_{w\in W}|w|+|T|\log |\Sigma|),其中 |T| 是字典树结点数)。

后缀数组

定义排列 \operatorname{SA}_w(1),\ldots,\operatorname{SA}_w(|w|) 表示:w 的第 i 小的非空后缀为 w[\operatorname{SA}_w(i),|w|]

定义排列 \operatorname{ISA}_w(1),\ldots,\operatorname{ISA}_w(|w|)\operatorname{SA}_w(1),\ldots,\operatorname{SA}_w(|w|) 的逆排列。有时 \operatorname{ISA} 也写作 \operatorname{Rank}

对于 2\leq i\leq |w|,定义 \operatorname{Height}_w(i)=\operatorname{LCP}(w[\operatorname{SA}_w(i-1),|w|],w[\operatorname{SA}_w(i),|w|])

结论:\operatorname{LCP}(w[\operatorname{SA}_w(x),|w|],w[\operatorname{SA}_w(y),|w|])=\min_{i=x+1}^y \{\operatorname{Height}_w(i)\},其中 x<y

我们可以 O(|w|) 地建立 w 的 SA,但是常用算法是 O(|w|\log |w|) 的。

后缀树

w 的所有后缀组成的集合为 W_sW_s 的字典树称为 w 的后缀字典树。

将字典树上所有度数为 2 的非根结点缩去(缩到边上),得到的树称为 w 的隐式后缀树。

如果只缩那些不对应 w 的后缀的结点,那么得到的树称为 w 的后缀树(注意,这只是本文的约定)。

也有一种说法是在 w 的末尾加上一个不同于 w 中任何字符的字符 \$,得到 \hat w,其(按上述定义的)后缀树称为 w 的(显式)后缀树。

类似我们在 AC 自动机中所做的,可以求出失配数组 B_x,在这里称为后缀链接,记为 \operatorname{lk}(x)

结论:后缀树结点数为 O(|w|)

后缀自动机

能接收 w 所有子串的最小 DFA,称为 w 的后缀自动机(SAM)。

\operatorname{Endpos}(u)\operatorname{Startpos}(u) 分别表示 uw 中作为子串出现的左端点集合和右端点集合。

结论:后缀自动机状态数和转移数为 O(|w|)

结论:后缀自动机的每个结点代表一族 \operatorname{Endpos} 相等的子串,它们是其中最长者的最长的一部分后缀。

结论:\operatorname{Endpos} 集合只有不交和包含两种关系,以此可以建出一棵树,称为 SAM 的树结构,这棵树等价于反串 \operatorname{Rev}(w) 的后缀树。

我们可以 O(|w|\log |\Sigma|) 地建立 w 的后缀树和 SAM。

回文树 / 回文自动机

w 的每个回文子串对应到一个结点,另外建立两个特殊结点分别称为奇根和偶根。令字符串 u 对应的结点为 f(u),则:|u|=1f(u) 的父结点为奇根;|u|=2f(u) 的父结点为偶根;否则 f(u) 的父结点为 f(u[2,|u|-1])。这将得到一个有两个根的森林结构,称为回文树。

对于一个回文串 u,将其长度为 \lceil\frac{|u|}{2}\rceil 的后缀称为其半串。回文树可以看作是所有回文子串对应的半串的 Trie(但对长度为奇数和偶数的回文串有特殊的标识分开),于是我们可以构造它的 ACAM,这样构造出的自动机结构称为回文自动机(PAM),类似可以定义失配指针 B_x 和失配树。

结论:回文串的所有 Border 都是回文串,回文子串 u 的最长 Border 等于其最长回文真后缀,为 f(u) 在回文自动机上失配指针指向的结点对应的回文子串。

我们可以 O(|w|\log |\Sigma|) 地建立 w 的 PAM。

Manacher 算法

我们可以 O(|w|) 地对于每个 i\in [1,|w|] 求出最大的 j 使得 w[i-j+1,i+j-1] 是回文串。

Z 函数

我们可以 O(|w|) 地对于每个 i\in [1,|w|] 求出 \operatorname{LCP}(w,w[i,|w|])

3. 区间 Border / 基本子串字典

论文:陈孙立,子串周期查询问题的相关算法及其应用,IOI 2019 集训队论文集

基本子串字典被引入 OI 可能主要是为了解决区间 Border 问题,即:

根据 Border 的相关知识,我们知道 w[l,r] 的 Border 可以表示成 O(\log(r-l+1)) 个等差数列的(不相交)并,我们希望得到一种 O(\log(r-l+1)) 的算法求出这些等差数列(作为 Border 集合的一种表示)。

对于任意字符串 w,我们之前已经给出了一个结论:w 的长度在 (x,2x] 间的 Border 构成等差数列。于是现在我们的想法就是把子串 w[l,r] 的 Border 长度划分为 (1,2],(2,4],(4,8] 这些由 2 的幂为端点的区间,每一段区间内 Border 都占据一个等差数列。现在我们考虑如何求长度处在 (2^k,2^{k+1}] 中的所有 Border。

pu 的最小周期。注意到 p_1+p_2\leq |u|\longrightarrow \min(p_1,p_2)\leq \frac{|u|}{2},所以其中必有一个 p 的倍数,那么按照和上面类似的推理过程可以得到下述推论:

让我们回到需要求解的问题:求出 w[l,r] 的长度在 (2^k,2^{k+1}] 中的所有 Border。

考虑 u=w[l,l+2^{k+1}-1],\ u_0=[l,l+2^k-1],\ v=w[r-2^{k+1}+1,r],\ v_0=w[r-2^k+1,r],设 u_0v 中的出现位置集合为 S_1v_0u 中出现位置集合为 S_2。那么每有一个 (2^k,2^{k+1}] 中的 Border x,就必有对应的一个 S_1S_2 中的元素(即由 x 是 Border 蕴含的 u_0v 中的出现以及 v_0u 中的出现),因此将 S_1,S_2 平移后求交就是 Border 集合了。

根据引理 1,S_1,S_2 都是等差数列,但下面我们要得出一个更强的结论:

于是如果我们已知 S_1,S_2,就可以 O(1) 求出这两个等差数列的交(如果其中有一个大小不超过 2 则平凡,否则公差相同容易合并)。下面考虑如何 O(1) 求出 S_1,S_2

这就要用到基本子串字典。以 S_1 为例,我们实际上要找到以 [r-2^{k+1}+1,r-2^k] 开头的长度为 2^k 的子串中有哪些等于 u_0。而对于一个等差数列,只需要找到首项、末项和第二项就可以得到整个数列,所以我们只需找到使得 i\in [r-2^{k+1}+1,r-2^k]N_k(i)=N_k(l)i 的最小、次小、最大值即可。

将序列以 2^k 为块长分块,则每一块中使得 N_k(i)=N_k(l)i 都应该是个等差数列(因为总可以表示成“某个子串的长度在 (2^k,2^{k+1}] 中的 Border 集合”的形式),设第 x 块中这个等差数列是 s(l,x),查询时我们只需求出至多两块(因为查询区间长度是 2^k)然后将它们的结果拼接即可。那么某一个 s(l,x) 如何查询呢?这可以使用哈希表完成:因为 s(l,x)\ne \varnothing(l,x) 只有 O(n) 种(因为总共只有 n-2^k+1 个长度为 2^k 的子串)。

于是,我们 O(\log(r-l+1)) 地支持了查询区间 Border 集合,将哈希表换为二分可以得到一个也许稍微简洁的 O(\log(r-l+1)\log n) 算法。

基本子串字典还有一些其他应用,例如:

论文中还提到了一种基于基本字串字典的求 w 所有本原 k 次方串的方法(k\ge 3),但这方面的应用似乎可以被 Runs 理论全面取代,因此这里不做介绍。

4. 对称压缩 SAM / 基本子串结构

论文:徐翊轩,浅谈压缩后缀自动机,IOI 2020 集训队论文集

论文:许庭强,一类基础子串数据结构,IOI 2023 集训队论文集

基本子串结构基本子串字典基本上是完全没有关系的两个东西。

在前置知识中我们介绍了后缀自动机。后缀自动机是接收 w 所有子串的最小 DFA,那么不知大家在初学 SAM 时是否有过这样的疑问:为什么它叫后缀自动机而不是子串自动机之类的?这里我们尝试从这个问题出发,引入本节想要介绍的对称压缩 SAM 和基本子串结构。

后缀自动机的特点是每个结点代表的是一系列字符串,且它们都是其中最长者的后缀,这或许可以作为名称的由来(尽管本身可能不是这么来的)。我们换一个视角来看,SAM 是一个“向左扩展”的自动机,因为任意给定一个子串 w[l,r]w[l,r] 对应的 SAM 结点上的最长字符串一定是某个 w[l',r],使得 w[l',r]w[l,r]\operatorname{Endpos} 相同,且 l' 最小。那么 w[l',r] 可以看成是在 w[l,r] 左边反复加字符,直到再加字符会改变其出现位置(我们可以用 \operatorname{Endpos} 来描述出现位置,也可以直接用出现次数来描述)。

于是我们想:有没有向右扩展的自动机呢?当然这就是反串的 SAM。那么有没有向两侧扩展的自动机呢?

首先要说明 \operatorname{Lext},\operatorname{Rext},\operatorname{Ext} 是良定义的。前面已经说过了 \operatorname{Lext}(u) 其实就是 u 对应 SAM 结点对应的最长子串;那么同理 \operatorname{Rext}(u) 相应地就是 u 对应反串 SAM 结点对应的最长子串,而 \operatorname{Ext} 可以看成由 \operatorname{Lext}(u)\operatorname{Rext}(u) 拼接而成。可以直观理解为:\operatorname{Ext}(u) 是每次 u 出现时前后一定会连带着一起出现的那部分东西。

那么现在我们想建立一种结构,使得 u 可以直接链接到 \operatorname{Ext}(u),或者说把一族 \operatorname{Ext} 相同的子串放到一起(例如 SAM 就是把一族 \operatorname{Lext} 相同的子串放到一起)。

我们从 SAM 出发构建这一结构,SAM 已经完成了向左扩展,只需再实现向右扩展即可。对于 SAM 上的一个结点 x,如果其出度大于 1,那么它一定是不可向右扩展的,因为它右侧的内容不唯一;如果它代表的是某个后缀,那么它一定也是不可向右扩展的,因为后缀的右边没东西了。但是如果 x 不代表后缀,且出度等于 1,那么就说明它每次出现后面接的字符是相同的(就是唯一的出度上的那个字符),这代表 x 可以和它的出边指向的点 y 合并,它们对应的 \operatorname{Ext} 相同。重复这样的合并,就有:

注意,上面的论文中将这个东西称为“完整压缩后缀自动机”,相应地本文中的后缀树在论文中被称为“完整后缀树”,而论文中相应的“压缩后缀自动机”和“后缀树”对应的是本文中的隐式压缩后缀自动机(即把所有出度为 1 的点都合并掉,不管是不是后缀)和隐式后缀树。

下图展示了 \texttt{abbab} 和它的反串 \texttt{babba} 的 SAM 和压缩 SAM,灰色结点为后缀对应的结点:

压缩 SAM 虽然是一个两侧扩展的自动机,但是它的转移边仍然是单向的——沿着转移边只能从前往后加字符。不过注意到反串 SAM 也可以压缩,并且得到的压缩 SAM 和正串压缩 SAM 应该是结点一一对应的(因为每个结点都是一个 \operatorname{Ext} 等价类,而 \operatorname{Ext} 和正反无关)。所以可以提出以下更有对称性的结构:

对称压缩 SAM 上有两种不同类的转移,一种是向后加字符,另一种是向前加字符。因此,对称压缩 SAM 相比普通 SAM 在处理字符串问题上的优势就体现为:能够解决同时需要往左右两边加字符的问题

对称压缩 SAM 刻画了不同的 \operatorname{Ext} 等价类之间的转移关系,但是很多问题要求我们针对一个等价类内部的性质进行研究。\operatorname{Ext} 等价的结构比 \operatorname{Endpos} 等价这样的结构要复杂一些,下面介绍一种用来解释 \operatorname{Ext} 等价类内部性质及它们之间的关系的一种字符串整体结构——基本子串结构。

我们将 w 的所有子串画在一个 |w|\times |w| 网格图中,横坐标表示子串的左端点,纵坐标表示右端点,然后将处于压缩 SAM 上同一结点的子串染为同种颜色,得到的结构就称之为(初步的)基本子串结构,每个同色连通块称为一个

下图展示了 \texttt{abbab} 的正反压缩 SAM、对称压缩 SAM 和基本子串结构:

以同种颜色边框围成的区域就是一个块,本质不同的块与压缩 SAM 上的结点一一对应。而压缩 SAM 上的边即可看成横向或纵向(分别对应反串压缩 SAM 和正串压缩 SAM)相邻的两块之间的转移,这种转移关系在上图也画出来了。

根据对压缩 SAM 的讨论,我们可以得到如下结论:

于是,对于一个块 C,可以定义其左上角的格子对应的串为其代表元 \operatorname{Rep}(C)

对于一个块 C,定义 h(C) 为其左边界的高度,w(C) 为其上边界的宽度。

有了结论 3,我们还可以进一步研究 SAM 上的后缀链接对应到基本子串结构上的结果。根据上面的对应,SAM 上的后缀链接就发生在横向相邻的两块的交界处,反串 SAM 上的后缀链接就发生在纵向相邻的两块的交界处。

结论 4 是很多算法复杂度的保证。例如,如果在每一块上有一个 O((h(C)+w(C))\operatorname{polylog}(h(C)+w(C))) 的算法,则对每一块做一遍的话,这个算法的复杂度就是 O(|w|\operatorname{polylog} |w|)

之前我们介绍了用基本子串字典处理区间 Border 的方法,而基本子串结构给了另一种看待区间 Border 问题的视角。

子串 w[l,r] 的 Border 即同时为它的前缀和后缀的串的集合,而这相当于是 w[l,r] 在正串 SAM 上的链接树祖先链与反串 SAM 上的链接树祖先链的交。现在让我们考虑对两棵链接树都进行重链剖分,然后我们按照这种重链剖分来确定一种特定的每个块在网格图上的位置。我们知道每个块在网格中出现了多次,我们希望给每个块选择其中一个它出现的位置,使得所有重链上的后缀链接都是直接的,即都是同一行/列的两块之间的转移。

这(给块选位置)对于后续的算法并不是必要的,但它能直观地告诉我们一些性质,所以下面我们递归地给出一种构造,以正串 SAM 的链接树为例:从叶子出发,叶子的出现次数当然都是 1,它们的位置确定。对于每个非叶结点,将它安放在它的重儿子放在的位置的右侧(不难发现一个块中所有 SAM 结点对应的重儿子可以认为都来自同一块,事实上同一块中所有 SAM 结点对应的子树是完全同构的)。

下图展示了 \texttt{abbab} 的正反串 SAM 链接树的重链剖分和对应的基本子串结构中块的位置,可以看到,重边都是横平竖直的。

对于正串 SAM 链接树上一条重链,令其编号为其叶结点所在的行;对于反串,则是叶结点所在列。

下面我们回到区间 Border 问题,现在问题已经转化为寻找两棵树上两个祖先链的交,我们先把祖先链转化为 O(\log |w|) 条重链段,虽然两条祖先链就会有 O(\log^2 |w|) 个重链对,但是显然只有其中 O(\log |w|) 对可能会相交(因为相交至少要长度相等,然后可以双指针一下),我们分别考虑每一对。

考虑每个正串 SAM 上的点,它对应的是基本子串结构某个块的一行,于是其中所有串就属于这一块中的不同的列,根据结论 5,这些列对应的反串 SAM 结点所在重链编号连续,所以每个正串 SAM 结点对应的是一个反串 SAM 重链编号的区间,而我们要查询的就是正串 SAM 某条重链上有多少个点对应的区间包含某个给定的反串重链。

但是注意我们考虑的并不是一整条重链,而只是重链的一个前缀(靠近根的部分),这可以通过加上一维长度来限制,即我们所求的是正串 SAM 一条重链和反串 SAM 一条重链的交集中长度 \leq l 的那部分。于是我们把上面的对应关系扩展一下,每个正串 SAM 结点对应的是一个反串 SAM 重链编号、长度为坐标轴的二维平面中,一条斜率为 1 的斜线段。

以区间 Border 计数为例,对于每条正串 SAM 重链,它对应的数据结构问题可以描述为:给定二维平面中若干个斜率为 1 的斜线段,若干次询问一条平行于 y 轴的直线段与多少个斜线段有交,这可以简单地转化为一个二维数点。从而我们用基本子串结构也解决了区间 Border 问题,但复杂度相比基本子串字典多个 \log(因为每个询问要进行 O(\log |w|) 次二维数点询问)。

5. Lyndon 分解 / Significant Suffix

论文:万成章,浅谈与 Lyndon 理论有关的字符串组合问题,IOI 2022 集训队论文集

Lyndon 理论是用来解决与字典序相关的问题的一套理论,不过在代数上它似乎有别的用处,但这和本文无关。

由于 w 一定比它的后缀长,所以对于 Lyndon 串 w 必然有 w\lhd w[i,|w|]

再介绍一个和 Lyndon 串有关的概念。对于字符串 w,形如 w[i,|w|]+w[1,i-1]\ (i\in [1,|w|]) 的串称为 w 的一个循环表示,如果 w 是其所有循环表示中最小的,则称 w 是一个 Necklace(项链)

对于字符串 w,如果存在一系列 Lyndon 串 w_1,\ldots,w_k 满足 w_i\ge w_{i+1}w=w_1+w_2+\ldots +w_k,则称 w=w_1+w_2+\ldots w_kwLyndon 分解,每个 w_iw 的一个 Lyndon 因子

我们下面证明,一个串 w 的 Lyndon 分解是存在且唯一的。

根据引理 1 可以说明 Lyndon 分解的存在性:初始时令每个字符都是一个 Lyndon 因子,然后不断合并相邻的满足 w_i<w_{i+1} 的 Lyndon 因子 w_i,w_{i+1},最后就得到了一个 Lyndon 分解(在下一节中,我们将给出一个以此为基础的 Lyndon 分解算法)。

根据引理 2 可以说明 Lyndon 分解的唯一性:最后一个 Lyndon 因子总是最小非空真后缀,删掉它后递归即可。至此我们成功证明了 Lyndon 分解是唯一存在的。

我们可以将 Lyndon 分解中相邻相同的 Lyndon 因子写在一起,成为 w=w_1^{p_1}\ldots w_k^{p_k} 的形式,其中 w_1>w_2>\ldots >w_k

Duval 算法的正确性可以直接验证——它分解出的每个子串确实都是 Lyndon 串,且字典序确实递减。

Duval 算法的复杂度不难证明。每当分解出一段 Lyndon 因子 w_i^{p_i} 时,i 会回滚 w 的一个前缀,从而回滚的总长度不超过 \sum |w_i|,从而不超过 |w|,因此 i 前进的次数也是 O(|w|),每次前进可以 O(1) 完成判断,而分解的总复杂度是 O(|w|),所以整个算法的复杂度为 O(|w|)

Significant Suffix 有一个性质:如果 w'\in \operatorname{SS}(w),那么不存在另一个后缀 w'' 使得 w''\lhd w' 的那些后缀 w',这是比较好理解的:w''\lhd w' 可以推出 w''v\lhd w'v

一些与字典序相关的问题可以归于对 Significant Suffix 的讨论,所以我们首先介绍其一些性质。

w 的分解为 w=w_1^{p_1}w_2^{p_2}\ldots w_k^{p_k},以下引理揭示了 Lyndon 分解与 Significant Suffix 的联系:

\operatorname{SS}(w) 从短到长排序为 u_1,\ldots,u_t,那么对于任意 i,j 都不能有 u_i\lhd u_ju_j\lhd u_i,从而只能 u_1\sqsubset u_2\sqsubset \ldots \sqsubset u_t

现在考虑 s_1,\ldots,s_k(在引理 4 中定义)中哪些可能成为 Significant Suffix,根据上面的讨论,一个必要条件显然是 s_{i+1}\sqsubset s_i

于是我们说明了 Significant Suffix 只会在 s_\lambda,\ldots,s_k 中诞生(事实上,它们全都是 Significant Suffix,但这通常不很关键),我们由此分析一下 |\operatorname{SS}(w)|:由引理 5 知 s_{i+1}\sqsubset w_i,从而,|s_i|>2|s_{i+1}|,所以 |s_\lambda|>2^{k-\lambda}|s_k|,而 |s_\lambda|\leq |w|,所以 k-\lambda< \log_2(|w|),于是:

现在让我们回答一个基本的问题:给定 v,求最小的 w'v,其中 w'w 的后缀,将答案记为 \operatorname{Minsuf}(w,v)。为此,我们首先引入一个比较规则:

对于 i\ge \lambda,我们知道 s_{i+1}\sqsubset w_i。因此设 w_i=s_{i+1}y_i(令 s_{k+1}=\varepsilon),再设 x_i=y_is_{i+1},那么有 s_i=s_{i+1}x_i^{p_i}。现在我们想知道哪个 s_iv 是最小的,那么可以比较一下 s_ivs_{i+1}v,去掉公共前缀 s_{i+1} 后就是要比较 x_i^{p_i}vv。由引理 6,我们知道 x_i^{p_i}v<v\iff x_i^{\infty}<v

因此只要我们找到一个 x_{i-1}^{\infty}>v>x_i^{\infty},那么对于所有 \lambda\leq j\leq i-1 都有 x_j^{\infty}>v,对于所有 i\leq j\leq k 都有 x_j^{\infty}<v,于是我们就有:

许多字典序相关问题都可以转化为求 \operatorname{Minsuf}(w,v),例如最小后缀就是 v=\varepsilon;最大后缀就是字典序取反后 v=\texttt{z}\texttt{z} 是一个极大字符);最小循环表示就是 v=w

6. 幂串 / Runs

7. 杂技