边做这题边在怀疑人生,我去年是怎么学会后缀数组的。。为什么现在看着一(九)脸茫然QAQ。
这题首先贪心策略显然:每次从头或尾取一个较小的加入。然后对于头尾相同的,我们应该判断下一位的大小。而如果暴搜的话,估计AAAAAAAAAAAQAAAAAAAAAAA的数据会被卡成dog。
我们需要快速判断一个后缀和一个前缀的大小。
比如AABCAA:
用f[i]表示从第i个开始整个串的后缀(比如f[1] = AABCAA)
用g[i]表示从第i个开始整个串的前缀(比如g[4] = CBAA)
那么当我们遇到相同的情况下,比较f[L]和g[R],若f[L]>g[R]则证明后者更优。
维护后缀的关系考虑后缀数组,而前缀只要把原串反写一下转化为求后缀就好了。最后根据rank数组判断。
注意:cc数组不要开小了QAQ因此WA了两发
#include<bits/stdc++.h>
using namespace std;
int len;
int cc[60005],t1[60005],t2[60005],sa[60005],rank[60005];
char s[60005];
inline char read()
{
char c=getchar();
while (c<'A'||c>'Z') c=getchar();
return c;
}
inline bool cmp(int *y,int a,int b,int l)
{
int arank1=y[a];
int brank1=y[b];
int arank2=a+l>=len?-1:y[a+l];
int brank2=b+l>=len?-1:y[b+l];
return arank1==brank1&&arank2==brank2;
}
inline void make_sa()
{
int *x=t1,*y=t2;
int m=27;
for (int i=0;i<m;i++) cc[i]=0;
for (int i=0;i<len;i++) ++cc[x[i]=s[i]];
for (int i=1;i<m;i++) cc[i]+=cc[i-1];
for (int i=len-1;i>=0;i--) sa[--cc[x[i]]]=i;
for (int k=1;k<len;k<<=1)
{
int p=0;
for (int i=len-k;i<len;i++) y[p++]=i;
for (int i=0;i<len;i++)
if (sa[i]>=k) y[p++]=sa[i]-k;
for (int i=0;i<m;i++) cc[i]=0;
for (int i=0;i<len;i++) ++cc[x[y[i]]];
for (int i=1;i<m;i++) cc[i]+=cc[i-1];
for (int i=len-1;i>=0;i--) sa[--cc[x[y[i]]]]=y[i];
swap(x,y);
m=1; x[sa[0]]=0;
for (int i=1;i<len;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?m-1:m++;
if (m>=len) break;
}
}
int main()
{
scanf("%d",&len);
for (int i=0;i<len;i++) s[i]=read();
for (int i=0;i<len;i++) s[i]-=('A'-1);
s[len]=0;
for (int i=1;i<=len;i++) s[len+i]=s[len-i];
len=len*2+1;
make_sa();
for (int i=0;i<len;i++) rank[sa[i]]=i;
int L=0,R=len/2+1;
for (int i=1;i<=(len>>1);i++)
{
if (rank[L]<rank[R]) putchar(s[L++]+'A'-1);
else putchar(s[R++]+'A'-1);
if (!(i%80)) puts("");
}
return 0;
}