Lyndon 分解
现在只会 lyndon 分解怎么写。所以先放在这里占坑。以后补 Runs 和 Lyndon Tree 相关知识。
大量抄 pdf 和 cmd 博客。
Lyndon Word 及其相关性质
定义:若字符串 \(s\) 的最小后缀是它本身,则它是一个 Lyndon Word。
- Lyndon Word 没有 Border。
证明:如果存在,则 border 作为后缀比它小。
2. 若两个字符串 \(u,v\) 为 Lyndon Word,且 \(u<v\),则 \(uv\) 也是 Lyndon Word。
证明:首先显然开头在 \(|u|\) 前的后缀都比 \(uv\) 大。若 \(|u|\ge|v|\),则在 \(|v|\) 前 \(uv\) 和 \(v\) 就出现了不同,则 \(v>uv\),同时 \(v\) 是 Lyndon Word,则 \(uv\) 的所有后缀都大于它。若 \(|u|<|v|\),则若 \(u\) 不是 \(v\) 前缀,则显然 \(v>uv\)。反之,将 \(v\) 前面 \(|u|\) 个字符去掉大于 \(v\) ,则 \(v>uv\)。
- 若字符串 \(s\) 和字符 \(x\) 满足 \(sx\) 为某个 Lyndon Word 的前缀,则对 字符 \(y>x\),\(sy\) 是 Lyndon Word。
证明:设 \(sxt\) 是 Lyndon Word,则对于 \(i\ge 2\),\(s[i\rangle xt>sxt\),即 \(s[i\rangle x>s\),因此 \(s[i\rangle y>s\)。而 \(y>x>s_1\),因此 \(sy\) 是 Lyndon Word。
Lyndon 分解
定义:一个串 \(s\) 的 Lyndon 分解为一个字符串序列 \(t_1,t_2,\cdots,t_m\),满足:
- \(t_i\) 为 Lyndon Word。
- \(t_i\ge t_{i+1}\)。
定理:Lyndon 分解存在且唯一。
存在性:构造即证明。初始令 \(m=|s|\),每一个 \(t_i\) 都是单字符。每次找到 \(a_i<a_{i+1}\) 并合并为一个串,最后停止的时候即是合法的 Lyndon 分解。
唯一性:假设存在两个 Lyndon 分解 \(A,A'\),设 \(|A_i|>|A'_i|\),此时 \(A_i=A'_iA'_{i+1}\cdots A'_k\langle l]\)。那么显然有 \(A_i<A'_k\langle l]\le A'_k\le\cdots\le A'_i<A_i\) 矛盾。
Duval 算法
Duval 算法可以在 \(O(n)\) 时间复杂度和 \(O(1)\) 的额外空间内求得一个串的 Lyndon 分解。它维护三个变量 \(i,j,k\),满足任意时刻:
- \(s\langle i]\) 的 Lyndon 分解已经固定。
- \(s[i,k-1]=t^x+v(x>1)\) 的分解没有固定,\(t\) 是 Lyndon Word,且 Lyndon 分解确定的上一个串 \(t_g>s[i,k-1]\)。\(j=k-|t|\)。
当前处理到 \(k\),分三种情况讨论:
- \(s_j=s_k\) 时,\(j,k\) 都 \(+1\),仍然不变。
- \(s_j<s_k\) 时,\(v+s_k\) 是 Lyndon Word,则不断往前合并,整个 \(s[i,k]\) 固定为 Lyndon Word。
- \(s_j>s_k\) 时,\(t^x\) 固定为 Lyndon Word,\(i\) 调整至 \(v\) 的开头。
显然三个指针都是单调的,因此复杂度 \(O(n)\)。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char s[5000010];
int n,ans;
int main(){
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;){
int j=i,k=i+1;
while(k<=n&&s[j]<=s[k]){
if(s[j]<s[k])j=i;
else j++;
k++;
}
while(i<=j){
ans^=i+k-j-1;
i+=k-j;
}
}
printf("%d\n",ans);
return 0;
}