[洛谷P1368]工艺
题意
给定一个环,从任意一个点出发形成一个串,求这个串的最小字典序
思路
O(n)做法最小表示法,不会,再见
按照处理环的套路,先倍长了再说
后缀数组SA
显然就是求最小的后缀,但是要注意这个后缀长度必须不小于n,后缀排序裸题(因为数据过水所以不需要离散化)
后缀自动机SAM
(据说这道题被卡了但还是提一下)
对倍长后的串建SAM之后,从root向下走最小的边走n次即可
Code;
#include<bits/stdc++.h>
#define N 600005
using namespace std;
int n,m,len,a[N],sa[N],rk[N],tp[N],h[N],cnt[N];
template <class T>
void read(T &x)
{
char c;int sign=1;
while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
void radix_sort()
{
for(int i=0;i<=m;++i) cnt[i]=0;
for(int i=1;i<=len;++i) cnt[rk[i]]++;
for(int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
for(int i=len;i>=1;--i) sa[cnt[rk[tp[i]]]--]=tp[i];
}
void get_sa()
{
m=200;
for(int i=1;i<=len;++i) rk[i]=a[i],tp[i]=i;
radix_sort();
for(int t=1;t<len;t<<=1)
{
int p=0;
for(int i=1;i<=t;++i) tp[++p]=len-t+i;
for(int i=1;i<=len;++i) if(sa[i]>t) tp[++p]=sa[i]-t;
radix_sort();
swap(rk,tp);
rk[sa[1]]=p=1;
for(int i=2;i<=len;++i) rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+t]==tp[sa[i-1]+t]) ? p : ++p;
if(p==len) break;
m=p;
}
}
void get_h()//可以不要qwq
{
int ht=0;
for(int i=1;i<=len;++i) rk[sa[i]]=i;
for(int i=1;i<=len;++i)
{
int p=sa[rk[i]-1];
while(a[i+ht]==a[p+ht]) ++ht;
h[rk[i]]=ht;
ht=max(0,ht-1);
}
}
int main()
{
read(n);len=n*2;
for(int i=1;i<=n;++i) read(a[i]),a[i+n]=a[i];
get_sa();
get_h();
int con=0,ans=len+1;
for(int i=1;i<=len;++i)
{
if(con&&h[i]<n) break;
if(sa[i]<=n) {con=1;ans=min(ans,sa[i]);}
}//相同的串取更前面那个,不过这道题并没有什么用。。。。。。
for(int i=ans;i<ans+n;++i) printf("%d ",a[i]);
return 0;
}