1. 程式人生 > >POJ 3581-Sequence(字尾陣列)

POJ 3581-Sequence(字尾陣列)

Sequence
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 6485 Accepted: 1429
Case Time Limit: 2000MS

Description

Given a sequence, {A1A2, ..., An} which is guaranteed AA2, ..., 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 {A1A2, ..., An} and {B1B2, ..., Bn}, we say {A1A2, ..., An} is smaller than {B1B2, ..., Bn} if and only if there exists such i ( 1 ≤ i ≤ n) 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}

Source

題目意思:

有一個數組,分割成三份,每份翻轉後,得到的最小的字典序?

解題思路:

字尾陣列。先確定第一段,因為A1是最大的,所以求反轉之後字串中字典序最小的字尾。 再將剩餘部分分成兩段,計算兩個原序列並將得到的新序列反轉後的字尾陣列,從中選取字典序最小的合適的字尾。 cin超時,輸入輸出外掛WA,scanf才AC…坎坷…
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
#define maxn 200100

int n,k,a[maxn];
int rev[maxn],sa[maxn];
int rank[maxn*2],tmp[maxn*2];

bool compare_sa(int i,int j)//倍增法,比較rank
{
    if(rank[i]!=rank[j]) return rank[i]<rank[j];
    else
    {
        int ri=i+k<=n?rank[i+k] :-1;
        int rj=j+k<=n?rank[j+k] :-1;
        return ri<rj;
    }
}

void construct_sa(int s[],int N,int sa[])//計算s的字尾陣列
{
    for(int i=0; i<=N; ++i)//初始長度為1,rank為字元編碼
    {
        sa[i]=i;
        rank[i]=i<N?s[i] :-1;
    }
    for(k=1; k<=N; k*=2)//倍增法求字尾陣列
    {
        sort(sa,sa+N+1,compare_sa);
        tmp[sa[0]]=0;
        for(int i=1; i<=N; ++i)
            tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0);
        for(int i=0; i<=N; ++i)
            rank[i]=tmp[i];
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=0; i<n; ++i)
        cin>>a[i];
    //solve();
    reverse_copy(a,a+n,rev);//a不變,反轉a之後存入rev陣列
    construct_sa(rev,n,sa);//求字尾陣列
    int p1;
    for(int i=0; i<n; ++i)//確定第一段的分割位置
    {
        p1=n-sa[i];
        if((p1>=1)&&n-p1>=2) break;
    }
    //cout<<"p1="<<p1<<endl;
    int m=n-p1;
    reverse_copy(a+p1,a+n,rev);
    reverse_copy(a+p1,a+n,rev+m);
    construct_sa(rev,m*2,sa);
    int p2;
    for(int i=0; i<=2*m; ++i)//確定第二段的分割位置
    {
        p2=p1+m-sa[i];
        if((p2-p1>=1)&&n-p2>=1) break;
    }
    //cout<<"p2="<<p2<<endl;
    reverse(a,a+p1);
    reverse(a+p1,a+p2);
    reverse(a+p2,a+n);
    for(int i=0; i<n; ++i)
        cout<<a[i]<<endl;
    return 0;
}

/**
5
10
1
2
3
4
**/