后缀数组 POJ 3581 题解

Language:
Sequence
Time Limit: 5000MSMemory Limit: 65536K
Total Submissions: 9000Accepted: 2036
Case Time Limit: 2000MS

Description

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}

Source

由于第一个数最大,所以第一部分分割特别简单,只需要考虑将字符串反转后的最小后缀字符串即可。
剩余两部分切割,由于不是相互独立的

|-------→|→|
A       BC  D

假设在AD字符串选取一段AB反转后,再选取第二段CD反转,得到的字符串是最小字典序的。
|←------|←|
B       AD  C

那么这种选择相当于,将AD复制一倍变成
|-------→|→||-------→|→|
A       BC DA        BC  D

反转后
|←|←-------||←|←-------|
D CB      AD CB        A
   |--需要的---|

题解:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 2e5+1;

int N,k,n;
int SA[MAXN*2],tempSA[MAXN*2];
int RA[MAXN*2],tempRA[MAXN*2];
int A[MAXN],rev[MAXN*2];

bool cmp_sa(int i,int j){
	if(RA[i]!=RA[j]){
		return RA[i] < RA[j];
	}else{
		int l = i+k<n?RA[i+k]:-1;
		int r = j+k<n?RA[j+k]:-1;
		return l<r;
	}
}

void get_sa(const int *num,int len){
	n = len;
	for(int i=0;i<n;i++) {
		RA[i]=num[i];
		SA[i]=i;
	}
	int cnt = 1;
	for(k=1;k<n;k<<=1){
		sort(SA,SA+n,cmp_sa);
		tempRA[SA[0]] = -1;
		for(int i=1;i<n;i++){
			tempRA[SA[i]] = tempRA[SA[i-1]] + (cmp_sa(SA[i-1],SA[i])?1:0);
		}
		for(int i=0;i<n;i++){
			RA[i] = tempRA[i];
		}
	}
}

int main(){
	scanf("%d",&N);
	for(int i=0;i<N;i++) scanf("%d",&A[i]);
	reverse_copy(A,A+N,rev);
	get_sa(rev,N);
	int p1;//第一段分割位置 
	for(int i=0;i<N;i++){
		p1 = N-SA[i];
		if(p1>=1 && p1<=N-2) break;
	}
	int m = N - p1;
	reverse_copy(A+p1,A+N,rev);
	reverse_copy(A+p1,A+N,rev+m);
	get_sa(rev,m*2);
	int p2;
	for(int i=0;i<2*m;i++){
		p2 = p1 + m - SA[i];
		if(p2-p1>=1 && p2 <=N-1) break; 
	}
	reverse(A,A+p1);
	reverse(A+p1,A+p2);
	reverse(A+p2,A+N);
	for(int i=0;i<N;i++) printf("%d\n",A[i]);
	return 0;
}
posted @ 2019-07-18 01:40  zz2108828  阅读(172)  评论(0编辑  收藏  举报