Lyndon 分解
樣例
樣例輸入 1
ababa
樣例輸出 1
2 4 5
樣例輸入 2
bbababaabaaabaaaab
樣例輸出 2
1 2 4 6 9 13 18
樣例輸入 3
azAZ0129
樣例輸出 3
2 4 8
數據范圍與提示
\(1\le |s| \le 2^{20}\)
OZY的題解
冷門東西,但是今天考到了,做個記錄。
記號
\(s[l : r]\) 表示字符串\(s\) 從第\(l\) 個字符到第\(r\) 個字符的子串(從\(1\) 開始標號),\(|s|\) 表示\(s\) 的長度。
當\(l = 1\) 時\(s[l : r]\) 簡寫為\(s[: r]\) ,表示\(s\) 的一個前綴。當\(r = |s|\) 時\(s[l : r]\) 簡寫為\(s[l :]\) ,表示\(s\) 的一個后綴。
\(st, s +t\) 表示兩個字符串\(st\) 的拼接,\(s^k\) 表示\(k\) 個\(s\) 拼起來,特別地,\(s^{\infty}\) 表示\(s\) 的無限循環。
定義
Lyndon 串:如果一個串\(s\) 滿足\(s = \min\{s[i :]|1 \le i \le |s|\}\) 那么我們稱串\(s\) 為Lyndon 串。定義字符串的大小關系就是字典序的大小關系
性質
當\(u,v\)均為Lyndon Words,且\(u<v\),那么\(uv\)也是一個Lyndon Words。
證明還是比較顯然的,這里就不證了
Lyndon 划分
對於一個字符串\(s\),如果一個划分將它分成若干個串\(s=p_1+p_2+p_3+\dots+p_n\),使得每個\(p\)都是Lyndon Words,且\(p_i\ge p_{i+1}\),則這個划分是Lyndon 划分。
可以發現,一個字符串,一定存在一種Lyndon 划分,證明可以用構造法來證明。
一開始先所有\(p\)設為單個字母。顯然,這是滿足第一個條件的,只需要再滿足遞減的關系就可以了。
可以發現若\(p_i<p_{i+1}\),它們合起來也是一個Lyndon Words。
並且可以發現,對於一個串,他的Lyndon 划分是唯一的。
算法
目的是求出\(r[i]\),表示第\(i\)個字符所屬Lyndon Words的右端點的下一個位置。
就是維護類似單調棧的東西就可以了。單調棧內維護的是屬於同一Lyndon Words的節點,換句話說如果不滿足字典序的單調遞增,就要清空。發現這就是維護定義……很顯然啊。
復雜度瓶頸在於比較后綴大小,用后綴樹(DC3后綴數組+笛卡爾樹)和±1RMQ即可\(O(n)\)。這里只給出不能AC的hash做法,\(O(n\log n)\)。
當然這題還有\(O(n)\)的Duval算法,但是我覺得沒必要學。
#include<bits/stdc++.h>
#define co const
#define il inline
using namespace std;
typedef unsigned long long ULL;
co int N=(1<<20)+10;
co ULL base=131;
char str[N];int n;
ULL pw[N],hs[N];
il ULL calc(int l,int r){
return hs[r]-hs[l-1]*pw[r-l+1];
}
int lcp(int x,int y){ // str[x:],str[y:]
int l=0,r=n-max(x,y)+1;
while(l<r){
int mid=(l+r+1)>>1;
if(calc(x,x+mid-1)==calc(y,y+mid-1)) l=mid;
else r=mid-1;
}
return l;
}
il bool cmp(int x,int y){ // str[x:]<str[y:]
int len=lcp(x,y);
if(len==n-max(x,y)+1) return x>y; // partition by >=
return str[x+len]<str[y+len];
}
int r[N],st[N],top;
int main(){
scanf("%s",str+1),n=strlen(str+1);
pw[0]=1;
for(int i=1;i<=n;++i){
pw[i]=pw[i-1]*base;
hs[i]=hs[i-1]*base+str[i];
}
for(int i=1;i<=n;++i){
while(top&&cmp(i,st[top])) r[st[top--]]=i;
st[++top]=i;
}
while(top) r[st[top--]]=n+1;
for(int i=1;i<=n;i=r[i]) printf("%d ",r[i]-1);
return 0;
}