【bzoj1692】【队列变幻】【贪心+后缀数组】

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
题解:
首先如果左右单词不一样肯定是选字典序小的加入.
如果一样的话可以比较以左边字符开头的后缀和以右边字符开头的前缀。 
所以把模板串反过来接到后面,中间加入一个分隔符.建立后缀数组.
然后前缀就可以转化成后缀直接比较了.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 60010
using namespace std;
int s[N],c[N],t1[N],t2[N],sa[N],rank[N],n,m,temp;
char ch;
bool cmp(int *y,int p,int q,int k){
  int a=(p+k>=n)?-1:y[p+k];
  int b=(q+k>=n)?-1:y[q+k];
  return a==b&&y[p]==y[q];
}
void build_sa(int m){
  int *x=t1,*y=t2;
  for (int i=0;i<m;i++) c[i]=0;
  for (int i=0;i<n;i++) c[x[i]=s[i]]++;
  for (int i=1;i<m;i++) c[i]+=c[i-1];
  for (int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
  for (int k=1;k<=n;k<<=1){
    int p=0;
    for (int i=n-k;i<n;i++) y[p++]=i;
    for (int i=0;i<n;i++) if (sa[i]>=k) y[p++]=sa[i]-k;
    for (int i=0;i<m;i++) c[i]=0;
    for (int i=0;i<n;i++) c[x[y[i]]]++;
    for (int i=1;i<m;i++) c[i]+=c[i-1];
    for (int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    swap(x,y);
    p=1;x[sa[0]]=0;
    for (int i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
    if (p>=n) break;
    m=p; 
  } 
}
int main(){
  scanf("%d",&n);s[n]='$';
  for(int i=0;i<n;i++){
   scanf("%*c%c",&ch);
   s[2*n-i]=s[i]=ch;
  }
  m=n;n=n+n+1;build_sa(2666); 
  for (int i=0;i<n;i++) rank[sa[i]]=i;
  //for (int i=0;i<n;i++) cout<<sa[i]<<' ';
  int l=0,r=m-1;
  while (l<r){
    int a=rank[l],b=rank[m+m-r];
    //cout<<l<<' '<<r<<' '<<a<<' '<<b<<' ';
    if (a<b){
      printf("%c",s[l]);l++;
    }
    else{
      printf("%c",s[r]);r--;
    }
    temp++;
    if (temp>=80&&(l!=r)){puts("");temp=0;}
  }
  printf("%c",s[l]);
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值