Lyndon 分解
Lyndon 串
其严格最小后缀为其本身的串,称为 \(Lyndon\) 串,也就是 \(s\) 是其所有循环移位中严格最小的。
\(s,t\) 都为 \(Lyndon\) 串是,且 \(s<t\),则 \(st\) 也为 \(Lyndon\) 串。证明 \(t>st\) 后即可证明该性质。
Lyndon 分解
\(Lyndon\) 分解为将 \(s\) 分解得 \(s=s_1s_2s_3 \dots s_k\),且 \(s_i \geqslant s_{i+1}\),\(Lyndon\) 分解是存在且唯一的。
存在性由 \(Lyndon\) 串的性质不难得到,唯一性可以用反证法来证明。
Duval 算法
\(Duval\) 算法可以 \(O(n)\) 求出一个串的 \(Lyndon\) 分解。
对于字符串 \(s\) 和字符 \(c\),若 \(sc\) 是某个 \(Lyndon\) 串的前缀,则对于字符 \(d>c\) 有 \(sd\) 是 \(Lyndon\) 串。
维护三个指针 \(p,i,j\)。\([1,p-1]\) 是已经进行完 \(Lyndon\) 分解的部分,\([p,j-1]\) 为 \(s_1s_2s_3 \dots s_kt\),其中 \(t\) 可以为空串,且 \(s_1\) 是 \(Lyndon\) 串和 \(s_1=s_2= \dots =s_k\),\(t\) 是 \(s_1\) 的前缀,满足 \(s[p,j-1]\) 比上一个分解出的串小。
当前加入字符 \(s_j\),令 \(i=j-|s_1|\),即为 \(t\) 在循环串对应的位置。进行分类讨论:
当 \(s_i=s_j\) 时,\(t\) 能继续匹配,让 \(i,j\) 都向后移动一个位置。
当 \(s_i<s_j\) 时,得 \(s[p,j]\) 为 \(Lyndon\) 串,将 \(i\) 移动到 \(p\),\(j\) 向后移动一个位置。
当 \(s_i>s_j\) 时,得 \(s_1s_2s_3 \dots s_k\) 为分解后的串,然后继续考虑 \(t\),\(j\) 向后移动一个位置。
因为 \(p\) 只向后移动,\(j\) 向前移动的距离不超过 \(p\) 向后移动的距离,所以复杂度为 \(O(n)\)。
while(p<=n)
{
int i=p,j=p+1;
while(j<=n&&s[i]<=s[j]) i=s[i]==s[j++]?i+1:p;
while(p<=i) p+=j-i,ans^=p-1;
}
这段代码求的是 \(Lyndon\) 分解所有右端点的异或和。
应用
求解最小循环表示,\(O(n)\) 对所有 \(s[1,i]\) 求最小最大后缀。