Description
FJ打算带他的N(1 <= N <= 30,000)头奶牛去参加一年一度的“全美农场主大奖赛”。在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们从裁判席前依次走过。 今年,竞赛委员会在接受队伍报名时,采用了一种新的登记规则:他们把所有队伍中奶牛名字的首字母取出,按它们对应奶牛在队伍中的次序排成一列(比如说,如果FJ带去的奶牛依次为Bessie、Sylvia、Dora,登记人员就把这支队伍登记为BSD)。登记结束后,组委会将所有队伍的登记名称按字典序升序排列,就得到了他们的出场顺序。 FJ最近有一大堆事情,因此他不打算在这个比赛上浪费过多的时间,也就是说,他想尽可能早地出场。于是,他打算把奶牛们预先设计好的队型重新调整一下。 FJ的调整方法是这样的:每次,他在原来队列的首端或是尾端牵出一头奶牛,把她安排到新队列的尾部,然后对剩余的奶牛队列重复以上的操作,直到所有奶牛都被插到了新的队列里。这样得到的队列,就是FJ拉去登记的最终的奶牛队列。 接下来的事情就交给你了:对于给定的奶牛们的初始位置,计算出按照FJ的调整规则所可能得到的字典序最小的队列。
Input
* 第1行: 一个整数:N
* 第2..N+1行: 第i+1行仅有1个'A'..'Z'中的字母,表示队列中从前往后数第i 头奶牛名字的首字母
Output
* 第1..??行: 输出FJ所能得到的字典序最小的队列。每行(除了最后一行)输 出恰好80个'A'..'Z'中的字母,表示新队列中每头奶牛姓名的首 字母
Sample Input
6
A
C
D
B
C
B
输入说明:
FJ有6头顺次排好队的奶牛:ACDBCB
Sample Output
ABCBCD
输出说明:
操作数 原队列 新队列
#1 ACDBCB
#2 CDBCB A
#3 CDBC AB
#4 CDB ABC
#5 CD ABCB
#6 D ABCBC
#7 ABCBCD
HINT
首先我们可以得到一个贪心的想法:
每次从头和尾之间取最优,即局部最优,然后得到一个串。
那么为什么这是正确的呢?……
我们分两种情况讨论。
1:头<尾或者头>尾
这种情况下,选什么应该是很明显的。
假如我们选了更大的那个,换成另一个总能得到更优的解。
所以这种情况下我们直接就可以作出选择。
2:头=尾
这个时候我们不能马上做出选择了……
但是我们知道要选择一个后面更优的。
比如说ABCA,那么显然我们会选择取头的A,因为AB<AC.
在上面的例子中,
不妨换个角度:ABCA<ACBA.
即从头开始到len位置,和从尾开始到1位置的两个串,它们之间有一个大小的比较。
我们可以看到这样子的比较的结果,
其实就是我们想要人为进行比较的结果。
对于2,我们想要相等时,指针推进,直到分出胜负为止;显然这是会TLE的。
但是刚才的想法,让我们能够想到后缀排序方面。
我们只要让原串反过来,再接到原串的后方,接着求出这个串的sa和rank即可。
举个简单的例子:刚才的ABCA。
首先我们接一个字符,这个字符不要是A..Z即可,我接了[。
那么变成ABCA[ACBA
然后当我们选择原串1,4位置的时候,
其实4位置对应了后面的6位置;
所以判断1,6位置的rank大小即可。显然rank[1]<rank[6],那么取1.
以此类推。
注意此题有坑点啊……
输出的格式非常难受= =
气死了每80个一行WA了一次。。。
然后发现末尾还必须换行又WA了一次……
#include<bits/stdc++.h>
using namespace std;
const int
N=60006; //N<<1
int n;
char s[N];
int cnta[N],cntb[N],a[N],b[N];
int tsa[N],sa[N],rank[N<<1];
void Get_SA(){
for (int i=0;i<27;i++) cnta[i]=0;
for (int i=1;i<=n;i++) cnta[s[i]-'A']++;
for (int i=1;i<27;i++) cnta[i]+=cnta[i-1];
for (int i=n;i;i--) sa[cnta[s[i]-'A']--]=i;
rank[sa[1]]=1;
for (int i=2;i<=n;i++)
rank[sa[i]]=rank[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
for (int j=1;rank[sa[n]]!=n;j<<=1){
for (int i=1;i<=n;i++) a[i]=rank[i],b[i]=rank[i+j];
for (int i=0;i<=n;i++) cnta[i]=cntb[i]=0;
for (int i=1;i<=n;i++) cnta[a[i]]++,cntb[b[i]]++;
for (int i=1;i<=n;i++) cnta[i]+=cnta[i-1],cntb[i]+=cntb[i-1];
for (int i=n;i;i--) tsa[cntb[b[i]]--]=i;
for (int i=n;i;i--) sa[cnta[a[tsa[i]]]--]=tsa[i];
rank[sa[1]]=1;
for (int i=2;i<=n;i++)
rank[sa[i]]=rank[sa[i-1]]+
(a[sa[i]]!=a[sa[i-1]] || b[sa[i]]!=b[sa[i-1]]);
}
}
int main(){
scanf("%d",&n);
getchar();
for (int i=1;i<=n;i++)
s[i]=getchar(),getchar();
s[n+1]='['; //'z'+1
for (int i=n;i;i--) s[n+n-i+2]=s[i];
n=n<<1|1;
Get_SA();
int m=n>>1,tm=m,L=1,R=1;
while (tm--){
if (rank[L]<rank[R+m+1]){
printf("%c",s[L]);
L++;
} else{
printf("%c",s[R+m+1]);
R++;
}
if (!((m-tm)%80)) puts("");
}
if (m%80) puts("");
return 0;
}