题目链接:https://vjudge.net/problem/POJ-3581
Sequence
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
{10, 1, 2, 3, 4} -> {10, 1 | 2 | 3, 4} -> {1, 10, 2, 4, 3}
Source
POJ Founder Monthly Contest – 2008.04.13, Yao Jinyu
题意:
给出一个字符串,将其分成三段,并且将每一段翻转。求翻转过后字典序最小的一个。
题解:
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\n", pos1, pos2);
105 for(int i = pos1; i>=0; i--) printf("%d\n", a[i]);
106 for(int i = pos2; i>pos1; i--) printf("%d\n", a[i]);
107 for(int i = n-1; i>pos2; i--) printf("%d\n", a[i]);
108 }
View Code