题目大意:给定一个字符串,每次取头或者尾放在新字符串里,求字典序最小的新字符串
首先如果两边的字符不一样 那么肯定要选择小的放在新字符串里
但如果两边一样 比如CCBACC 肯定从尾取比较优 原因是CCA比CCB要小
于是我们把原串反写接在后面变成[email protected] 然后跑一遍后缀数组 每次就能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(""); } }
时间: 2024-10-10 08:25:02