题目大意:给定一个字符串,每次取头或者尾放在新字符串里,求字典序最小的新字符串
首先如果两边的字符不一样 那么肯定要选择小的放在新字符串里
但如果两边一样 比如CCBACC 肯定从尾取比较优 原因是CCA比CCB要小
于是我们把原串反写接在后面变成CCBACC@CCABCC 然后跑一遍后缀数组 每次就能O(1)比较两个子串的大小了
时间复杂度O(nlogn)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 60600
using namespace std;
int n;
char s[M];
int X[M],Y[M],sa[M],rank[M];
int sum[M],cnt[M],temp[M],tot;
inline char Get_Char()
{
char c;
do c=getchar(); while(c==' '||c=='\t'||c=='\n'||c=='\r');
return c;
}
void Get_Rank()
{
int i,j;
for(i=1;i<=n;i++)
sum[s[i]]++;
for(i=1;i<=127;i++)
sum[i]+=sum[i-1];
for(i=1;i<=n;i++)
sa[ sum[s[i]-1]+ ++cnt[s[i]] ]=i;
for(i=1;i<=n;i++)
{
if(i==1||s[sa[i]]!=s[sa[i-1]])
++tot;
rank[sa[i]]=tot;
}
}
void Radix_Sort(int key[],int order[])
{
int i;
for(i=0;i<=n;i++)
sum[i]=cnt[i]=0;
for(i=1;i<=n;i++)
sum[key[i]]++;
for(i=1;i<=n;i++)
sum[i]+=sum[i-1];
for(i=1;i<=n;i++)
temp[ sum[key[order[i]]-1]+ ++cnt[key[order[i]]] ]=order[i];
for(i=1;i<=n;i++)
order[i]=temp[i];
}
void Prefix_Doubling()
{
int i,j;
Get_Rank();
for(j=1;j<=n;j<<=1)
{
for(i=1;i<=n;i++)
{
X[i]=rank[i];
Y[i]=i+j>n?0:rank[i+j];
sa[i]=i;
}
Radix_Sort(Y,sa);
Radix_Sort(X,sa);
for(i=1,tot=0;i<=n;i++)
{
if( i==1 || X[sa[i]]!=X[sa[i-1]] || Y[sa[i]]!=Y[sa[i-1]] )
++tot;
rank[sa[i]]=tot;
}
}
}
int main()
{
int i,j=0;
cin>>n;
s[n+1]='A'-1;
for(i=1;i<=n;i++)
s[n+1-i]=s[n+1+i]=Get_Char();
n=n<<1|1;
Prefix_Doubling();
int l=1,r=n>>1;
while(l<=r)
{
++j;
if(s[l]<s[r]) putchar(s[l++]);
else if(s[l]>s[r]) putchar(s[r--]);
else if(rank[l]<rank[n+1-r]) putchar(s[l++]);
else putchar(s[r--]);
if(j%80==0) puts("");
}
}