题意:将一个长度为N(<=200000)的数组,划分成非空的三段,然后倒转每一段拼成一个新的数组(三段相对位置不变),输出字典序最小的结果。
思路:要求的就是倒转后字典序最小的结果,先倒转原数组,跑一次SA,之后后缀会按照字典序排列,我们只需要找到第一个满足sa[i]>1(因为要保证后面两段非空)的位置就行了,这就是第一段。
去掉第一段后,我们需要找出剩下的部分中字典序较小的部分作为第二段,直接找是不行的,参考下面两组数据:
9
8 4 -1 5 0 5 0 2 3
8
9 0 2 1 2 1 5 6
在输出-1 4 8后会认为0 5的字典序比0505小,会输出-1 4 8 0 5 3 2 0 5,然后实际上结果应该是-1 4 8 0 5 0 5 3 2.
为了解决这个问题,我们将剩下的数组复制一次接在原来的后面,再跑一次SA,这样再找就能保证找出的第二段会使结果字典序最小了,所以数组要开2倍。
题目中没有给数据范围,要先离散化到1-N。
此题最坑的地方就是多组case交C++会WA,只能交G++!!!
WA了两天才在discuss找到原因,一度自闭。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <cstdlib>
#include <set>
#include <map>
#include <vector>
#include <string>
using namespace std;
typedef long long ll;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn = 400005;
const int mod = 1000000007;
int s[maxn];
int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];
// 传入参数:str,sa,len+1,ASCII_MAX+1
void da(const int r[],int sa[],int n,int 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]]++;//以字符的ascii码为下标
for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
for(i=n-1; i>=0; i--) sa[--Ws[x[i]]]=i;
//for(int i=0;i<n+1;i++)cout<<sa[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]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
//x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
int sa[maxn];
struct node {
int a, id, idx;
}p[maxn];
bool c1(struct node a, struct node b) {
return a.a < b.a;
}
bool c2(struct node a, struct node b) {
return a.idx > b.idx;
}
int b[maxn];
int main() {
int n;
//while (~scanf("%d", &n)) {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &p[i].a);
p[i].idx = i;
s[i] = b[i] = p[i].a;
}
int mx = 1;
// 离散化
sort(p, p + n, c1);
for (int i = 0; i < n; ++i) {
p[i].id = (i == 0 ? 1 : (p[i].a == p[i - 1].a ? p[i - 1].id : p[i - 1].id + 1));
mx = max(mx, p[i].id);
}
sort(p, p + n, c2);
for (int i = 0; i < n; ++i) {
s[i] = p[i].id;
}
s[n] = 0;
da(s, sa, n + 1, mx + 2);
int pos, len;
for (int i = 1; i <= n; ++i) {
if (sa[i] > 1) {
len = pos = sa[i];
//printf("1 i=%d sa=%d\n", i, sa[i]);
// 第一段
for (int j = pos; j < n; ++j) {
printf("%d\n", p[j].a);
}
break;
}
}
// 复制剩下的数组
for (int i = 0; i < pos; ++i) {
s[i + pos] = s[i];
}
len <<= 1;
s[len] = 0;
da(s, sa, len + 1, mx + 2);
for (int i = 1; i <= len; ++i) {
if (sa[i] > 0 && sa[i] < pos) {
//printf("2 i=%d sa=%d\n", i, sa[i]);
// 第二段
for (int j = sa[i]; j < pos; ++j) {
printf("%d\n", p[j].a);
}
// 第三段
for (int j = 0; j < sa[i]; ++j) {
printf("%d\n", p[j].a);
}
break;
}
}
//}
return 0;
}
/*
5
10 1 2 3 4
4
3 2 1 2
5
9 7 3 9 1
9
8 4 -1 5 0 5 0 2 3
8
9 0 1 2 1 2 5 6
3
3 2 1
5
1 2 3 2 1
*/