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-2,...,ak, 问题转化为找一个分割位置m,使得

am,..ak, an-1,an-2,...,am+1,字典顺序最小。因此将原数列扩展成an-1,an-2,...,ak,an-1,an-2,...,ak,求后缀数组

这样,找到一个最小的i, 使得sa[i]>0, sa[i]<n-k(即要在前一部分),则m=sa[i]. 此时后缀sa[i]的前n-k个前缀刚好是

要求的翻转后的第二三部分。

另外就是要进行离散化,但要保证原数列之间的相对大小关系。

详细代码:

 1 #include <map>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #define rank rk
 6 using namespace std;
 7
 8 const int maxn=200010;
 9 int s[maxn];
10 int n;
11 int sa[maxn],t[maxn],t2[maxn],c[maxn];
12 // 后缀数组模板
13 void build_sa(int m){
14     int *x=t, *y=t2;
15     memset(c, 0, sizeof(int)*m);
16     for(int i=0; i<n; ++i) c[x[i]=s[i]]++;
17     for(int i=1; i<m; ++i) c[i]+=c[i-1];
18     for(int i=n-1; i>=0; --i) sa[--c[x[i]]]=i;
19     for(int k=1; k<=n; k<<=1){
20         int p=0;
21         for(int i=n-k; i<n; ++i) y[p++]=i;
22         for(int i=0; i<n; ++i)
23             if(sa[i]>=k) y[p++]=sa[i]-k;
24         memset(c, 0, sizeof(int)*m);
25         for(int i=0; i<n; ++i) c[x[y[i]]]++;
26         for(int i=1; i<m; ++i) c[i] += c[i-1];
27         for(int i=n-1; i>=0; --i) sa[--c[x[y[i]]]]=y[i];
28         std::swap(x,y);
29         y[n]=-1;
30         p=1; x[sa[0]]=0;
31         for(int i=1; i<n; ++i)
32             x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
33         m=p;
34         if(p>=n) break;
35     }
36 }
37 // 用于离散化
38 map<int,int> src2i, i2src;
39 map<int,int>::iterator it;
40
41 void print(int i, int t){
42     while(i<t){
43         printf("%d\n", i2src[s[i++]]);
44     }
45 }
46 int main(){
47     scanf("%d", &n);
48     for(int i=0; i<n; ++i){
49         scanf("%d", &t[i]);
50         src2i[t[i]]=1;
51     }
52     int cnt=1;
53     for(it=src2i.begin(); it!=src2i.end(); ++it){
54         i2src[cnt]=it->first;
55         it->second=cnt++;
56     }
57     for(int i=0; i<n; ++i)
58         s[n-1-i]=src2i[t[i]];
59     build_sa(maxn);
60     int idx1=sa[0], i=1;
61     while(idx1<2) idx1=sa[i++];
62     print(idx1, n);
63     copy(s, s+idx1, s+idx1);// 扩展数组
64     n=2*idx1;    build_sa(maxn);
65     int idx2=sa[0]; i=1;
66     while(idx2>=idx1 || idx2<1) idx2=sa[i++];
67     print(idx2, idx2+idx1);
68     return 0;
69 }
时间: 2024-12-16 10:06:21

poj 3581 Sequence(后缀数组,离散化)详解的相关文章

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 2217 Secretary (后缀数组)

题目大意: 计算两个字符串的最长的公共字符串字串的长度. 思路分析: 将两个串合并起来. 然后直接跑后缀数组求出height 然后就可以直接扫描一次height ,加个是不是在一个串中的判断就可以了. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define maxn 200005 using namespace std; char str[ma

POJ 1226 Substrings (后缀数组)

题目大意: 问的是m个字符串里,都出现过的子串.子串也可以出现在这个串的逆序串中. 思路分析: 居然wa在全5个 "a" 的数据上. 二分的时候下界不能为0.. 思路大致上是把原串和逆序串全部处理出来,放入str中,然后在每个串中间加一个没有出现过的. 此处注意输入不仅仅是字母. 然后跑一遍后缀数组. 然后用标记计数就好了. #include <iostream> #include <cstdio> #include <algorithm> #inc

指针数组与数组指针详解

转自:https://blog.csdn.net/men_wen/article/details/52694069 指针数组与数组指针详解 1.什么是指针数组和数组指针? 指针数组:指针数组可以说成是"指针的数组",首先这个变量是一个数组,其次,"指针"修饰这个数组,意思是说这个数组的所有元素都是指针类型,在32位系统中,指针占四个字节. 数组指针:数组指针可以说成是"数组的指针",首先这个变量是一个指针,其次,"数组"修饰这

JavaScript数组方法详解

JavaScript数组方法详解 JavaScript中数组的方法种类众多,在ES3-ES7不同版本时期都有新方法:并且数组的方法还有原型方法和从object继承的方法,这里我们只介绍数组在每个版本中原型上的方法,本文举例介绍了从ES3到ES7几乎所有的数组方法.这大概是最全的数组方法详解了.希望读者能从中有所收获. 一.各版本数组方法一览表 数组方法名 对应版本 功能 原数组是否改变 pop() ES3- 删除最后一位,并返回删除的数据 是 push() ES3- 在最后一位新增一或多个数据,

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 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可

最新java数组的详解

java中HashMap详解 http://alex09.iteye.com/blog/539545 总结: 1.就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java 对象放入数组中,只是把对象的引用放入数组中,每个数组元素都是一个引用变量. 2.HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置. 3.HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据 Has