(后缀数组)poj 3581 Sequence

Given a sequence, {A1A2, ..., An} which is guaranteed AA2, ..., 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 {A1A2, ..., An} and {B1B2, ..., Bn}, we say {A1A2, ..., An} is smaller than {B1B2, ..., 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}

这个题目的描述实在是太不详细,既没给范围、数据类型,也没有对“分成三部分”给出明确的定义。

在此根据自己因为题意不明出过的错误,阐明一下:

1、数据全是不超过int范围的非负数

2、“分成三部分”每一部分都不为空

希望后来做这个题目的同学如果看到这篇文章能不再因以上原因浪费不该浪费的时间。

首先将整个数组反过来看,由于第一个数是最大的,翻转之后只需要先找到此刻最小的后缀位置,就是第一个划分的区间。该位置需满足除该区间以外至少还剩余2个数。

之后再划分余下的部分。

注意到,如果一个圆环形式为[x,y][z,q] 将其两部分分别颠倒,得到[y,x] [q,z],通过整体翻转就又可以得到[x,y][z,q]的某一旋转形式。对于这样将一个大区间划分成两个小的之后再分别翻转,也是同理的。所以,可以把余下的部分再复制一份到后面(此时第一部分的已经去掉)。进行后缀数组的计算,找在非复制得到的位置的最小的后缀(且要保证余下至少1个数),就完成了对余下区间的划分。

 1 #include <iostream>
 2 #include <string>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <queue>
 8 #include <set>
 9 #include <map>
10 #include <list>
11 #include <vector>
12 #include <stack>
13 #define mp make_pair
14 #define MIN(a,b) (a>b?b:a)
15 #define rank rankk
16 //#define MAX(a,b) (a>b?a:b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 const int MAX=4e5+5;
20 const int INF=2147483647;
21 const int B=1024;//桶的大小
22 const double M=4e18;
23 using namespace std;
24 const int MOD=1e9+7;
25 typedef pair<int,int> pii;
26
27 int n,k;
28 int a[MAX],rank[MAX],tmp[MAX],sa[MAX];
29 bool cmp(int i,int j)
30 {
31     if(rank[i]!=rank[j])
32         return rank[i]<rank[j];
33     else
34     {
35         int ri=i+k<=n?rank[i+k]:-1;
36         int rj=j+k<=n?rank[j+k]:-1;
37         return ri<rj;
38     }
39 }
40 void construct_sa(int n)
41 {
42     if(n<=0)
43         return;
44     for(int i=1;i<=n;i++)
45     {
46         sa[i]=i;
47         rank[i]=a[i];
48     }
49     for(k=1;k<=n;k*=2)
50     {
51         sort(sa+1,sa+n+1,cmp);
52         tmp[sa[1]]=1;
53         for(int i=2;i<=n;i++)
54         {
55                 tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
56         }
57         for(int i=1;i<=n;i++)
58             rank[i]=tmp[i];
59     }
60 }
61 int main()
62 {
63     scanf("%d",&n);
64     for(int i=1;i<=n;i++)
65         scanf("%d",&a[i]);
66     reverse(a+1,a+1+n);
67     construct_sa(n);
68     int an=INF,lo=1;
69     int p1;
70     for(int i=1;i<=n;i++)
71     {
72         if(sa[i]<=n-1&&sa[i]>=3)
73         {
74             lo=sa[i];
75             break;
76         }
77     }
78     for(int i=lo;i<=n;i++)
79         printf("%d\n",a[i]);
80     for(int i=1;i<lo;i++)
81         a[lo+i-1]=a[i];
82     construct_sa(2*lo-2);
83     an=INF;int los=1;
84     for(int i=1;i<=2*lo-2;i++)
85     {
86         if(lo-1-sa[i]>=0&&sa[i]>=2)
87         {
88             los=sa[i];break;
89         }
90     }
91     for(int i=los;i<lo;i++)
92         printf("%d\n",a[i]);
93     for(int i=1;i<los;i++)
94         printf("%d\n",a[i]);
95 }
时间: 2024-10-12 18:39:22

(后缀数组)poj 3581 Sequence的相关文章

后缀数组 POJ 3581 Sequence

题目链接 题意:把n个数字(A1比其他数字都大)的序列分成三段,每段分别反转,问字典序最小的序列. 分析:因为A1比其他数字都大,所以反转后第一段结尾是很大的数,相当是天然的分割线,第一段可以单独考虑,即求整段的字典序最小的后缀.后面两段不能分开考虑, 例子: 98 4 -1 5 0 5 0 2 3第一步:3 2 0 5 0 5 -1 4 8 对应输出 -1 4 8第二步3 2 0 5 0 5(开始的时候我并没有复制一遍) 对应输出:0 5第三步3 2 0 5    对应输出: 3 2 0 5可

poj 3581 Sequence(后缀数组,离散化)详解

题目链接:http://poj.org/problem?id=3581 题目大意:给一个数列,要求将其分成三段,每段进行翻转后形成后合并成新数列,求按字典顺序最小的新数列. 思路: 注意到题目中数列a0,a2,a3...an-1, a0是最大的,因此将原数列翻转后an-1,an-2,...,a1,a0,求后缀数组, sa[0]所代表的后缀即为所求第一段翻转后的数列,注意到要分成三份,因此sa[0]<2时不可取,此时找sa[1], sa[2]看是否可取.找第一个位置后,设剩下 数列是an-1,an

POJ 3581 Sequence (后缀数组+离散化)

Sequence 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

后缀数组 POJ 1743 Musical Theme

题目链接 题意:给定n个数字,求超过5个数字的,最长的,变化相同的,不相交的重复子串 分析:男人8题中的一题!数列相邻两项做差,形成新数列,即求数列中的最长重复子串(不可相交). 后缀数组+二分答案.假如二分得到答案L,如何知道它是可行的呢? 因为对于排序后的后缀,Lcp ( Suffix ( List [ i ] ) , Suffix ( List [ i - 1 ] ) ) 是所有与Suffix ( List [ i ] )的LCP值中最大的一个. 因为 Height [ i ] 表示的是排

[贪心+后缀数组] poj 3623 Best Cow Line, Gold

题意: 给N个字符,每次只能取第一个或者最后一个,问构成最小字典序的串是什么. 思路: 贪心,每次取字典序最小的串,的首字符. 其实就是l和r比较一下构成的串那个字典序小. 这里运用后缀数组要实现O(1)的查询. 将原串反拼进串内做da. 然后根据ra判断. 代码: #include"cstdlib" #include"cstdio" #include"cstring" #include"cmath" #include&quo

POJ 3581 Sequence(后缀数组)

[题目链接] http://poj.org/problem?id=3581 [题目大意] 给出一个数列,将这个数列分成三段,每段分别翻转,使得其字典序最小,输出翻转后的数列. [题解] 首先,第一个翻转点就是翻转后数列的最小后缀,注意由于一定要分成三段,则至少要剩下两个元素.难点主要是如何处理第二个翻转点,我们发现剩余的部分的每一种翻转拆分都是将两串翻转后剩余部分拼接在一起得到的串的子串,所以我们将剩余部分翻转,复制一份拼接在后面,求最小后缀即可. [代码] #include <cstdio>

后缀数组 POJ 3261 Milk Patterns

题目链接 题意:可重叠的 k 次最长重复子串.给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠. 分析:与POJ 1743做法类似,先二分答案,height数组分段后统计 LCP>=m 的子串的个数. #include <cstdio> #include <cstring> #include <algorithm> const int N = 2e4 + 5; int sa[N], rank[N], height[N]; int t[N],

后缀数组 POJ 2217 Secretary

题目链接 题意:求两个字符串的最长公共子串 分析:做法是构造新的串是两个串连接而成,中间用没有出现的字符隔开(因为这样才能保证S的后缀的公共前缀不会跨出一个原有串的范围),即newS = S + '$' + T.对其求sa数组和height数组,取最小值的height[i],且两个后缀串属于不同的字符串. #include <cstdio> #include <cstring> #include <algorithm> #include <iostream>

后缀数组 poj 3415

首先,height[i]-k+1  很好理解把,他是说明目前这对后缀中不小于k的公共子串个数. 题解说用单调栈维护,为什么要用单调栈维护呢?因为时间复杂的可以大大降低. 怎么个降低方法呢? 在之前学习lcp(就是height数组)的时候,肯定接触过这样一个问题,就是从i开始的后缀字符串跟从j开始的后缀字符串的最长公共前缀. 计算方法是:取height[rank[i]+1]~height[rank[j]]的最小值 利用上面这个性质来维护单调递增的栈. 借助我看的那篇博客上面说的: 为什么要这样呢?