后缀数组 POJ 3581 题解
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 9000 | Accepted: 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 ≤ 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
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;
}