Lyndon 串:\(s\) 的字典序严格小于 \(s\) 的所有后缀的字典序。
性质:Lyndon 串是它的所有循环同构中最小的一个。
近似 Lyndon 串:若 \(s\) 为 Lyndon 串,则 \(xx....xx'\) 是近似 Lyndon 串,其中 \(x'\) 是 \(x\) 的前缀。
Lyndon 分解:将一个串 \(S\) 分解为 \(s_1s_2...s_k\),每一个串都是 Lyndon 串,且 \(s_i \ge s_{i+1}\)。
性质:Lyndon 分解是唯一的。对于两个 Lyndon 串 \(s,t\),如果 \(s<t\),则 \(st\) 是 Lyndon 串。
用后缀数组求解 Lyndon 分解
每次找 \(S\) 最小的后缀 \(S[i:]\),则在 \(S[:i]\) 的 Lyndon 分解后面加上一项 \(S[i:]\) 就是答案。
设原字符串为 \(S\) ,逐个加入字符,我们需要维护扫描的左端点 \(l\),右端点 \(r\) 和当前区间 \([l,r]\) 的 Lyndon 周期 \(d\)。
当 \(s[r]<s[r-d]\) 时,当前的近似 Lyndon 串即将结束,我们需要把它开头的那些完整的 Lyndon 串切下来,并且把最后剩余的一段重新处理,即令 \(r=l+1,d=1\)。
当 \(s[r]>s[r-d]\) 时,当前近似 Lyndon 串成为一个 Lyndon 串(它的 Lyndon 周期增大了),故 \(d=r-l+1\)。
当 \(s[r]=s[r-d]\) 时,新字符只是当前近似 Lyndon 串的延伸。
#include <bits/stdc++.h>
using namespace std;
string s;
signed main()
{
cin>>s;
int l=0,r=1,d=1;
s+='\0';
while(s[l])
{
if(s[r]<s[r-d])
{
while(l+d<=r) l+=d, cout<<l<<" ";
r=l,d=1;
}
else if(s[r]>s[r-d])
{
d=r-l+1;
}
++r;
}
}