poj 3581

题意:给你一个长度为n的数组A,第一个最大,要求你把它切成三段然后分别翻转,问你翻转完后的字典序最小的数组

分析:切成三段需要确定两个分割点,对于第一个分割点由于第一个数是最大的,那么只要求一下第一段翻转后字典序最小的就是答案,

求这个字典序最小第一段的方法就是对翻转后的A建立后缀数组,取第一个符合要求的就是第一个分割点(第一段长度至少1,后两段至少为2)

对于第二个分割点,不能简单的直接去最小的字典序,第二段会影响到第三段。将一个序列分两段然后分别翻转的结果可以看做,

将原序列拼接在一起后在翻转的字串。

这样就可以继续像前面一样用后缀数组搞了。

但是这题有两个坑点:

1、有多余数据,不能使用while(scanf("%d",&N))多case读入,而是直接scanf("%d",&N) 单case

2、A的数据范围没有给,是由负数的,所以在计算后缀数组时,要进行一定的修改,在找rank值的时候。

我的做法是在初始化rank的时候对A做一次离散化的处理,然后初始化rank。计算后缀数组的算法是倍增。具体的可以看代码。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <queue>
 6 #include <string>
 7 #include <vector>
 8 #include <set>
 9 #define maxn 200000*2+10
10 #define maxm 2010
11 #define maxt 110
12 #define INF 0x3f3f3f3f
13 #define mod 1009
14 #define MAX_STATE 2500
15 using namespace std;
16 int n,N,k;
17 int  Rank[maxn];
18 int  A[maxn];
19 int  B[maxn];
20 int SS[maxn];
21 int temp[maxn];
22 int SA[maxn];
23 //比较(rank[k,i],rank[k,i+k])与(rank[k,j],rank[k,j+k])
24 bool cmp_sa(int i,int j){
25     if(Rank[i]!=Rank[j])return Rank[i]<Rank[j];
26     else{
27         int ri = i+k<=n?Rank[i+k]:-1;
28         int rj = j+k<=n?Rank[j+k]:-1;
29         return ri<rj;
30     }
31 }
32 void construct_sa(int *S,int len, int *sa){
33     n = len ;
34     //长度为1的字符串,Rank直接取字符的编码
35     vector<int>v;
36     for(int i=0;i<=n;++i)v.push_back(S[i]);
37     sort(v.begin(),v.end());
38     v.erase(unique(v.begin(),v.end()),v.end());
39     for(int i=0;i<=n;++i){
40         sa[i]=i;
41         int pos = lower_bound(v.begin(),v.end(),S[i])-v.begin();
42         Rank[i]=i<n?pos:-1;
43     }
44     //利用对长度为k的字符串排序计算长度位2k的顺序
45     for(k=1;k<=n;k*=2){//注意这里不是 int k
46         sort(sa,sa+n+1,cmp_sa);
47         //计算新的rank,暂存到temp中
48         temp[sa[0]]=0;
49         //调整rank,相同的字符串的rank时一样的
50         for(int i=1;i<=n;++i){
51             temp[sa[i]] = temp[sa[i-1]]+ (cmp_sa(sa[i-1],sa[i])?1:0);
52         }
53         //存回rank
54         for(int i=0;i<=n;++i){
55             Rank[i]=temp[i];
56         }
57     }
58 }
59 void solve(){
60     reverse_copy(A,A+N,B);
61     construct_sa(B,N,SA);
62     int p1;
63     for(int i=0;i<=N;++i){
64         p1 = N-SA[i];
65         if(N-p1>=2&&p1>=1)break;
66     }
67     int m = N-p1;
68     reverse_copy(A+p1,A+N,B);
69     reverse_copy(A+p1,A+N,B+m);
70     construct_sa(B,m*2,SA);
71     int p2;
72     for(int i=0;i<=2*m;++i){
73         p2 = p1+m-SA[i];
74         if(p2-p1>=1&&N-p2>=1)break;
75     }
76     reverse(A,A+p1);
77     reverse(A+p1,A+p2);
78     reverse(A+p2,A+N);
79     for(int i=0;i<N;++i)printf("%d\n",A[i]);
80 }
81
82 int main(){
83     //freopen("in.txt","r",stdin);
84     //freopen("out.txt","w",stdout);
85     ios::sync_with_stdio(false);
86     scanf("%d",&N);
87     for(int i=0;i<N;++i)scanf("%d",&A[i]);
88     solve();
89 }

时间: 2024-10-06 18:19:45

poj 3581的相关文章

POJ 3581 Sequence(后缀数组)

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

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

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 defin

后缀数组 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 (后缀数组+离散化)

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 3581 后缀数组

点击打开链接 题意:将一个数列分成连续的三段,每段必须有数字,问这三段反转后的数列的最小字典序的方案,并输出,注意:第一个数比后面所有都大 思路:因为第一个数最大,那么将整个数列反转后的字典序最小的后缀为第一段分开位置,但是要判断情况,如最后还要至少剩下两个数完成后两段,接下来找第二段的分开位置,不可以像刚刚那么找了,想这个例子,将第一段去掉后是这样的,1 3 2 1 100 如果和第一次一样的方法结果是1 100 1 2 3 ,但是应该是1 2 3 1 100,前四个为第二段,那么给如何处理,

Sequence POJ - 3581 (后缀数组)

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 defin

[转] POJ字符串分类

POJ 1002 - 487-3279(基础)http://acm.pku.edu.cn/JudgeOnline/problem?id=1002题意:略解法:二叉查找数,map,快排... POJ 1200 - Crazy Search(基础)http://acm.pku.edu.cn/JudgeOnline/problem?id=1200题意:找出不相同的子串数量,字母表大小和子串长度会给定,这题很推荐hash入门者一做解法:hash(建议karp-rabin) POJ 1204 - Word

kuangbin带你飞 后缀数组 题解

2份模板 DC3 . 空间复杂度O3N 时间复杂度On #define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb)) #define G(x) ((x) < tb ? (x) * 3 + 1 :((x) - tb) * 3 + 2) const int MAXN = 300010; const int MAXM = 100010; char input[MAXM]; int wa[MAXN],wb[MAXN],ws[MAXN],wv[MAXN],wsd[MAX