POJ 3581 Sequence (后缀数组+离散化)

                                                                                                                       Sequence

Given a sequence, {A1, A2, ..., An} which is guaranteed A1 > A2, ..., An,  you are to cut it into three sub-sequences and reverse them separately to form a new one which is the smallest possible sequence in alphabet order.

The alphabet order is defined as follows: for two sequence {A1, A2, ..., An} and {B1, B2, ..., Bn}, we say {A1, A2, ..., An} is smaller than {B1, B2, ..., Bn} if and only if there exists such i ( 1 ≤ in) so that we have Ai < Bi and Aj = Bj for each j < i.

Input

The first line contains n. (n ≤ 200000)

The following n lines contain the sequence.

Output

output n lines which is the smallest possible sequence obtained.

Sample Input

5
10
1
2
3
4

Sample Output

1
10
2
4
3

Hint

{10, 1, 2, 3, 4} -> {10, 1 | 2 | 3, 4} -> {1, 10, 2, 4, 3}
 
题意:给定N个数字组成的序列 A1 A2....AN.其中A1比其他数字都大。 现在要把这个序列分成三段,并将每段分别反转,求能得到的字典序最小的序列是什么?  要求分得的每段都不为空
 
分析: 可以先将字符串反转,这样的话,因为A1比其他数字都大,找到后缀最小的位置,一定分隔出第一段.(如果没有这个条件的话,1123中第一段应分隔为11,但找最小后缀为1)
           对于剩下的部分不能直接找后缀最小的位置  比如  剩下两段为3 1 2 3 1 4,最后应得到132134,如果直接找后缀最小的位置就会变成134132,因为它反转后,最小的后缀是1 3,但我们应找的是 1 3 2 1 3,所以这个时候要保证剩下的两段最小的话,3 1 2 3 1 4反转为 4 1 3 2 1 3  ,反转后的部分会与剩下的部分相连接,所以我们在找后缀的时候将长度*2就能保证整体的字典序最小
          还有个问题就是这个题中,序列中ai的取值范围为int   
           在后缀数组中,最大的数应小于MAXN
           所以我们还需要进行离散化
           这里离散化的时候,我直接用的map,用时较高,但也没超时
代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
typedef long long ll;
using namespace std;
const int MAXN=400010;
int wa[MAXN],wb[MAXN],wv[MAXN],Ws[MAXN];
char str[MAXN];
int r2[MAXN];
int vis[MAXN];  //用来记录离散化之前数的大小
map<int,int>mp;
int cnt;
int insert1(int x)  //离散化操作
{
 if(mp.find(x)==mp.end())mp[x]=cnt++;
  return mp[x];
}
struct node
{
    int num;
    int id;
}r[MAXN];
int cmp2(node x,node y)
{
    return x.num<y.num;
}
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int r[],int sa[],int n,int m)  //n为len+1,m一般比数组中最大的数大一点即可
{
      int i,j,p,*x=wa,*y=wb,*t;
      for(i=0; i<m; i++) Ws[i]=0;
      for(i=0; i<n; i++) Ws[x[i]=r[i]]++;
      for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
      for(i=n-1; i>=0; i--) sa[--Ws[x[i]]]=i;
      for(j=1,p=1; p<n; j*=2,m=p)
      {
            for(p=0,i=n-j; i<n; i++) y[p++]=i;
            for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
            for(i=0; i<n; i++) wv[i]=x[y[i]];
            for(i=0; i<m; i++) Ws[i]=0;
            for(i=0; i<n; i++) Ws[wv[i]]++;
            for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
            for(i=n-1; i>=0; i--) sa[--Ws[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
                  x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
      }
      return;
}
int sa[MAXN],Rank[MAXN],height[MAXN];// sa是通过后缀排名找到它在字符串中的位置,rank是根据位置找到后缀排名,两者相逆,该模板中sa数组的最小值为1。

void calheight(int *r,int *sa,int n)
{
      int i,j,k=0;
      for(i=1; i<=n; i++) Rank[sa[i]]=i;
      for(i=0; i<n; height[Rank[i++]]=k)
            for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++);
}
int main()
{
  int n,maxx,tag,tag2,tag3,maxrank,len;
     scanf("%d",&n);
     cnt=1;
      maxx=0;
      for(int i=0;i<n;i++){
      scanf("%d",&r[i].num);
      r[i].id=i;
      }
      sort(r,r+n,cmp2);//从小到大的排序 为了进行离散化
     for(int i=0;i<n;i++){
        //r2[n-1-r[i].id]=r[i].num;
        r2[n-1-r[i].id]=insert1(r[i].num);//离散化
        vis[r2[n-1-r[i].id]]=r[i].num;
        maxx=max(maxx, r2[n-1-r[i].id]);
      }
      r2[n]=0;
     da(r2,sa,n+1,maxx+10);
     calheight(r2,sa,n);
       maxrank=MAXN+10;
     for(int i=2;i<n;i++)
     {
        if(Rank[i]<maxrank)
        {
          tag=i;            //分隔出第一段
          maxrank=Rank[i];
        }
     }
    // cout<<"  "<<tag<<endl;
      for(int i=tag;i<=n-1;i++)
        printf("%d\n",vis[r2[i]]);
      len=tag;
      for(int i=0;i<len;i++)
         r2[len+i]=r2[i];  //剩下的部分进行复制
      len=2*len;
      r2[len]=0;
      da(r2,sa,len+1,maxx+10);
      calheight(r2,sa,len);
       maxrank=MAXN+10;
    for(int i=1;i<len/2;i++)
    {
       if(Rank[i]<maxrank)
       {
           maxrank=Rank[i];
           tag2=i;
       }

    }
     for(int i=tag2;i<tag2+len/2;i++)
     {
         printf("%d\n",vis[r2[i]]);
     }
return 0;
}

 

     
            
posted @ 2017-09-04 22:36  hinata_hajime  阅读(147)  评论(0编辑  收藏  举报