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;
    }
}

相关文章: