题目链接:https://vjudge.net/problem/POJ-3581
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 7754 | Accepted: 1761 | |
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
题意:
给出一个字符串,将其分成三段,并且将每一段翻转。求翻转过后字典序最小的一个。
题解:
1.分成三段,即求出两个分割点,于是分两部分进行求解。
2.第一部分:求第一段。由于求的是翻转过后字典序最小的,那么先将原字符串翻转得到逆串,然后求其后缀数组。那么排名最靠前且满足能分成三段的那个后缀,即为翻转过后的第一段。 形如:1 1 2 2 3 3 等最小值重复出现在前面的串,如果直接求其逆串的后缀数组,那么排名最靠前的是“1”,而我们的目标是“1 1”,但题目说明了 A1>A2……An,因此不会出现这种情况。
3.第二部分:将剩余的逆串复制多一分接在后面,然后求其后缀数组,接下来的做法与第一步类似。
4. 为什么第二部分需要复制多一份在末尾,而第一部分不用呢?
答:由于题目声明了A1>A2……An,所以直接求其后缀数组,并得到排名最靠前且满足能分成三段的那个后缀,因为不会出现形如“1 1 2 2 3 3”的串,所以这个后缀一定是最优的;然而,去掉第一段之后,剩下的逆串的原串就可能为类似“1 1 2 2 3 3”的串,即逆串为“3 3 2 2 1 1”,如果直接求其后缀数组,那么得到的第二段为“1”,而我们的目标是“1 1”。为了解决这个问题,可以复制多一份接在后面得到“3 3 2 2 1 1 3 3 2 2 1 1”,然后求后缀数组,取位置在0~len-1且排名靠前且满足能分成两段的。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 4e5+100; 18 19 bool cmp(int *r, int a, int b, int l) 20 { 21 return r[a]==r[b] && r[a+l]==r[b+l]; 22 } 23 24 int r[MAXN], sa[MAXN], Rank[MAXN], height[MAXN]; 25 int t1[MAXN], t2[MAXN], c[MAXN]; 26 void DA(int str[], int sa[], int Rank[], int height[], int n, int m) 27 { 28 n++; 29 int i, j, p, *x = t1, *y = t2; 30 for(i = 0; i<m; i++) c[i] = 0; 31 for(i = 0; i<n; i++) c[x[i] = str[i]]++; 32 for(i = 1; i<m; i++) c[i] += c[i-1]; 33 for(i = n-1; i>=0; i--) sa[--c[x[i]]] = i; 34 for(j = 1; j<=n; j <<= 1) 35 { 36 p = 0; 37 for(i = n-j; i<n; i++) y[p++] = i; 38 for(i = 0; i<n; i++) if(sa[i]>=j) y[p++] = sa[i]-j; 39 40 for(i = 0; i<m; i++) c[i] = 0; 41 for(i = 0; i<n; i++) c[x[y[i]]]++; 42 for(i = 1; i<m; i++) c[i] += c[i-1]; 43 for(i = n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i]; 44 45 swap(x, y); 46 p = 1; x[sa[0]] = 0; 47 for(i = 1; i<n; i++) 48 x[sa[i]] = cmp(y, sa[i-1], sa[i], j)?p-1:p++; 49 50 if(p>=n) break; 51 m = p; 52 } 53 54 int k = 0; 55 n--; 56 for(i = 0; i<=n; i++) Rank[sa[i]] = i; 57 for(i = 0; i<n; i++) 58 { 59 if(k) k--; 60 j = sa[Rank[i]-1]; 61 while(str[i+k]==str[j+k]) k++; 62 height[Rank[i]] = k; 63 } 64 } 65 66 int a[MAXN], M[MAXN]; 67 int main() 68 { 69 int n; 70 scanf("%d", &n); 71 for(int i = 0; i<n; i++) 72 scanf("%d", &a[i]); 73 memcpy(M, a, sizeof(M)); 74 sort(M, M+n); 75 int m = unique(M, M+n)-M; 76 for(int i = 0; i<n; i++) //离散化 77 r[i] = upper_bound(M, M+m, a[i])-M; 78 reverse(r, r+n); 79 80 int pos1, pos2; 81 r[n] = m+1; //在翻转过后的串尾加个最大值,以保证 “1 1 2 2 3 3” 的情况下第一段取的是“2 2”, 而不是“2” 82 r[n+1] = 0; //再加上串尾结束符 83 DA(r, sa, Rank, height, n+1, m+2); 84 for(int i = 1; i<=n; i++) 85 if(n-1-sa[i]<=n-3) //第一段至多只能到n-3的地方,不然就不能分成三段了 86 { 87 pos1 = n-1-sa[i]; 88 break; 89 } 90 91 int len = n-pos1-1; 92 for(int i = 0; i<len; i++) //将剩下的串复制多一份接在后面,中间无需分隔符。 93 r[len+i] = r[i]; 94 len *= 2; 95 r[len] = 0; 96 DA(r, sa, Rank, height, len, m+2); 97 for(int i = 1; i<=len; i++) 98 if(sa[i]<len/2 && n-sa[i]-1<=n-2) //第二段至多只能到n-2的地方,不然就将剩下的分成两段了 99 { 100 pos2 = n-sa[i]-1; 101 break; 102 } 103 104 // printf("%d %d ", pos1, pos2); 105 for(int i = pos1; i>=0; i--) printf("%d ", a[i]); 106 for(int i = pos2; i>pos1; i--) printf("%d ", a[i]); 107 for(int i = n-1; i>pos2; i--) printf("%d ", a[i]); 108 }