样例输入 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}\)
冷门东西,但是今天考到了,做个记录。
\(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。
证明还是比较显然的,这里就不证了
对于一个字符串\(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;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章