poj3581Sequence(后缀数组)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud

Sequence

Time Limit: 5000MS   Memory Limit: 65536K
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 ≤ in) 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}

题意:

给出n个数,把这个数列分为三段,再把三段反转后连接在一起成为一个新串,求字典序最小的新串。

思路:

由于第一个数保证比其他所有数要大,在取第一段时直接取反转后的字典序最小的后缀即可。利用后缀数组即可求得。

而后把剩下的一个字符串中分成两段,使得其字典序最小。首先我们会很容易的想到向第一段一样的方法,即取反转之后的字典序最小的后缀。

但对于这种方法,我们很容易就能找到一组反例,即10 1 2 2 3

按照上述的方法,我们会取得第一段为10 1,反转,变为1 10

而后剩下的字符串为2 2 3,对于此串,反转之后为3 2 2,字典序最小的为2.整个串则变为1 10 2 3 2

显然当我们取2 2的时候整个串的字典序才是最小的,为1 10 2 2 3。

对于一个长度为m的串s[1]……s[m],设我们取将其分成s[1]……s[k]和s[k+1]……s[m]

将其反转之后则为s[k]……s[1]s[m]……s[k+1],我们可以发现,这个串正是s[m]……s[k+1]s[k]……s[1]s[m]……s[k+1]s[k]……s[1]的子串,并且我们可以通过找这个串的长度大于m字典序最小的后缀从而得到答案。

对于上例3 2 2,可变成3 2 2 3 2 2,长度大于m的字典序最小的后缀就是2 2 3 2 2。取其前一段即为所求的第二段。

另外,本题为单组输入,否则会WA,这点坑了我好久。。。。。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 using namespace std;
 5 #define MAXN 400010
 6 int n,k;
 7 int sa[MAXN],rank[MAXN],a[MAXN],b[MAXN],c[MAXN],tmp[MAXN];
 8 bool cmp(int i,int j){
 9     if(rank[i]!=rank[j])return rank[i]<rank[j];
10     else {
11         int ri=i+k<=n?rank[i+k]:-1e8;
12         int rj=j+k<=n?rank[j+k]:-1e8;
13         return ri<rj;
14     }
15 }
16 void build(int len,int *s){
17     n=len;
18     for(int i=0;i<=n;i++)sa[i]=i,rank[i]=i<n?s[i]:-1e8;
19     for(k=1;k<=n;k<<=1){
20         sort(sa,sa+n+1,cmp);
21         tmp[sa[0]]=0;
22         for(int i=1;i<=n;i++){
23             tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
24         }
25         for(int i=0;i<=n;i++)rank[i]=tmp[i];
26     }
27 }
28 int main()
29 {
30     int N;
31     scanf("%d",&N);
32     //while(scanf("%d",&N)!=EOF){
33         for(int i=0;i<N;i++)scanf("%d",a+i);
34         for(int i=0;i<N;i++)b[i]=a[N-1-i];
35         build(N,b);
36         int p1;
37         for(int i=0;i<=N;i++){
38             p1=N-sa[i]-1;
39             if(p1>=0&&p1+3<=n)break;
40         }
41         int m=N-p1-1;
42         for(int i=0;i<m;i++)c[i]=a[i+p1+1];
43         for(int i=0;i<m;i++)b[i]=b[i+m]=c[m-1-i];
44         build(2*m,b);
45         int p2;
46         for(int i=0;i<=2*m;i++)
47         {
48             p2=m-sa[i]-1;
49             if(p2>=0&&p2<=m-2)break;
50         }
51         p2+=p1+1;
52         for(int i=p1;i>=0;i--)printf("%d\n",a[i]);
53         for(int i=p2;i>p1;i--)printf("%d\n",a[i]);
54         for(int i=N-1;i>p2;i--)printf("%d\n",a[i]);
55     //}
56     return 0;
57 }

代码君

时间: 2024-10-26 07:21:46

poj3581Sequence(后缀数组)的相关文章

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

hdu5769--Substring(后缀数组)

题意:求含有某个字母的某个字符串的不同子串的个数 题解:后缀数组,记录每个位置距离需要出现的字母的距离就可以了.因为不太了解后缀模版卡了一会,还是很简单的. 记住sa和height数组都是1-n的下标. //后缀数组 #include <stdio.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll;

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

【tyvj1860】后缀数组

描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串.后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0你的任务就是求出SA和height这两个数组.字符串长度<=200000 输入格式 一行,为描述中的字符串(仅会出现小写

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the

hdu 4416 Good Article Good sentence(后缀数组&amp;思维)

Good Article Good sentence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2308    Accepted Submission(s): 649 Problem Description In middle school, teachers used to encourage us to pick up pre

uva 10829 - L-Gap Substrings(后缀数组)

题目链接:uva 10829 - L-Gap Substrings 题目大意:给定一个字符串,问有多少字符串满足UVU的形式,要求U非空,V的长度为g. 解题思路:对字符串的正序和逆序构建后缀数组,然后枚举U的长度l,每次以长度l分区间,在l和l+d+g所在的两个区间上确定U的最大长度. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using nam

poj 3693 Maximum repetition substring(后缀数组)

题目链接:poj 3693 Maximum repetition substring 题目大意:求一个字符串中循环子串次数最多的子串. 解题思路:对字符串构建后缀数组,然后枚举循环长度,分区间确定.对于一个长度l,每次求出i和i+l的LCP,那么以i为起点,循环子串长度为l的子串的循环次数为LCP/l+1,然后再考虑一下从i-l+1~i之间有没有存在增长的可能性. #include <cstdio> #include <cstring> #include <vector>