题解 P5334 【[JSOI2019] 节日庆典】
使用 Lyndon word 相关理论可以导出本题的一个简洁线性做法
zx 哥哥的线性做法,然而我并没有看懂.
首先考虑如果给定一个串
对
首先显然选择的位置一定是 Lyndon word 的开头,接下来我们断言如果选择了
证明:只需比较
u^2v,uv,v 的大小. 如果u\geq v ,那么自然有u^2v>uv>v ;否则,如果u 不是v 的前缀,那么有u^2v<uv<v ;如果u 是v 的前缀,那么我们可以把v 不断去掉前缀v 来变成之前的情况,从而uv 总是不优的. 注意这个证明并没有使用 Lyndon word 的任何性质,这对一般的字符串也是成立的.
注意到一个性质:如果
设
注意到这已经导出了一个
但我们不满足于此. 还是来考虑 Duval 算法,设当前考虑到的字符串为
- 如果
s_{|u'|+1}=c ,那么ans_i 为i-(|u|-ans_{|u'|+1}) 和1 中更优的那个 - 如果
s_{|u'|+1}<c ,那么ans_i=1 - 如果
s_{|u'|+1}>c ,那么等到以后再更新.
考虑其正确性,我们在什么时候会进行新的一轮遍历?答案是末尾的
现在我们只需要解决比较两个子串. 注意由于我们上面的理论,其中一个必然是另一个的后缀,于是只需要求一个后缀和原串的 lcp,可以用 exkmp 线性求出.
IO 时间差不多占了一半
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3e6+500;
char st[N];
int n;
int lcp[N];
int ans[N];
int chkmin(int x,int y,int r)
{
if(x==y)return x;
int len=lcp[x+r-y+1];//cout<<len<<endl;
if(len<y-x)return st[x+r-y+1+len]<st[len+1]?x:y;
len=lcp[y-x+1];
if(len>=x-1)return x;
else return st[len+1]<st[y-x+1+len]?x:y;
}
void exkmp()
{
lcp[1]=n;int mid=2,r=0;
for(int i=2;i<=n;i++)
{
if(i<=r)lcp[i]=min(lcp[i-mid+1],r-i+1);
else lcp[i]=0;
while(i+lcp[i]<=n&&st[1+lcp[i]]==st[i+lcp[i]])++lcp[i];
if(mid+lcp[i]-1>=r)mid=i,r=mid+lcp[i]-1;
}
// for(int i=1;i<=n;i++)cout<<lcp[i]<<" ";puts("");
}
char obuf[1<<25],*oh=obuf;
inline void out(int x){
if(!x){*oh++='0';return;}
static int buf[30];int xb=0;
for(;x;x/=10)buf[++xb]=x%10;
for(;xb;)*oh++=buf[xb--]|48;
}
int main()
{
n=fread(st+1,1,N-500,stdin);for(;!isalpha(st[n]);--n);
exkmp();
for(int i=1,j,k,u;i<=n;)
{
j=i,k=i+1,u=1;if(!ans[i])ans[i]=i;
for(;k<=n&&st[k]>=st[j];k++)
{
if(st[k]==st[j])
{
int t=(k-i)%u+i;
if(!ans[k])
{
if(ans[t]<i)ans[k]=i;
else ans[k]=chkmin(i,k-(t-ans[t]),k);
}
j++;
}
else
{
u=k-i+1;j=i;if(!ans[k])ans[k]=i;
}
}
i+=(k-i)/u*u;//cout<<i<<endl;
}
for(int i=1;i<=n;++i)out(ans[i]),*oh++=' ';*oh++='\n';
fwrite(obuf,1,oh-obuf,stdout);
}