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

Sequence
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 5738 Accepted: 1227
Case Time Limit: 2000MS

Description

Given a sequence, {A1, A2, ..., An} which is guaranteedA1 > 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 suchi ( 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}

Source

解析转自:http://blog.csdn.net/whyorwhnt/article/details/9840641

题意:给定一个数列,将其分为非空的三段。然后分别将三段的数字翻转连接在一起组成新的数列。输出处理后字典序最小的结果。

思路:首先注意必须写成单Case的形式!!!while(scanf("%d",&n)!=EOF)会RE或者WA。。。

因为要翻转,所以读入时直接反着读。由于没有告诉数值的上限,所以接下来离散化。

对于第一段:第一段翻转之后必须最小,因此对翻转过的数列求sa,则排名在前面且sa[i]>1(因为另两段不能为空)的第一个i为第一段。

接下来处理后两段:显然要让第二段的开头尽可能小,但是切去第二段后剩下的部分会直接接在第二段后面,所以不能直接求sa。为保证第三段接在第二段后面时整体最小,所以需要把剩余的串复制到剩余串的后面,再求sa.注意数组要开2倍。

以下举例转自 poj3581 Sequence(后缀数组) - Damonbaby的日志 - 网易博客

刚开始的时候觉得没有必要将剩下的数字复制一片贴在剩下的数字后面,所以wa了很多次。。。
最后在discuss里面看见一组数据:
9
8 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
可以看见这样做是不对的。。
必须要将剩下的字符串复制一遍贴在后面,然后再来求后缀数组。。。
正解:
第一步:
3 2 0 5 0 5 -1 4 8 对应输出 -1 4 8
第二步
3 2 0 5 0 5 3 2 0 5 0 5 对应输出: 0 5 0 5;
第三步
3 2 对应输出:3 2;

ac代码
Problem: 3581  User: kxh1995 
Memory: 8020K  Time: 2032MS 
Language: C++  Result: Accepted 
#include<stdio.h>                 
#include<string.h>                 
#include<algorithm> 
#include<stdlib.h>                
#include<iostream>                
#define min(a,b) (a>b?b:a)             
#define max(a,b) (a>b?a:b)     
#define INF 0xfffffff             
using namespace std;                     
int sa[400010],Rank[400010],rank2[400010],c[400010],*x,*y,s[400010],k,temp[400010],num[400010];     
void cmp(int n,int sz)          
{          
    int i;          
    memset(c,0,sizeof(c));          
    for(i=0;i<n;i++)          
        c[x[y[i]]]++;          
    for(i=1;i<sz;i++)          
        c[i]+=c[i-1];          
    for(i=n-1;i>=0;i--)          
        sa[--c[x[y[i]]]]=y[i];          
}          
void build_sa(int *s,int n,int sz)          
{          
    x=Rank,y=rank2;          
    int i,j;          
    for(i=0;i<n;i++)          
        x[i]=s[i],y[i]=i;          
    cmp(n,sz);          
    int len;          
    for(len=1;len<n;len<<=1)          
    {          
        int yid=0;          
        for(i=n-len;i<n;i++)          
        {          
            y[yid++]=i;          
        }          
        for(i=0;i<n;i++)          
            if(sa[i]>=len)          
                y[yid++]=sa[i]-len;          
            cmp(n,sz);          
			swap(x,y);          
			x[sa[0]]=yid=0;          
			for(i=1;i<n;i++)          
			{          
				if(y[sa[i-1]]==y[sa[i]]&&sa[i-1]+len<n&&sa[i]+len<n&&y[sa[i-1]+len]==y[sa[i]+len])          
					x[sa[i]]=yid;          
				else         
					x[sa[i]]=++yid;          
			}          
			sz=yid+1;          
			if(sz>=n)          
				break;          
    }          
    for(i=0;i<n;i++)          
        Rank[i]=x[i];          
}          
struct s
{
	int val,id;
}a[200020];
int cmp(const void *a,const void *b)
{
	return (*(struct s *)a).val-(*(struct s *)b).val;
}
int main()
{
	int n;
	scanf("%d",&n);
		int i;
		for(i=n-1;i>=0;i--)
		{
			scanf("%d",&num[i]);
			a[i].val=num[i];
			a[i].id=i;
		}
		qsort(a,n,sizeof(a[0]),cmp);
		temp[0]=1;
		for(i=1;i<n;i++)
		{
			temp[i]=(a[i].val==a[i-1].val?temp[i-1]:i+1);
		}
		for(i=0;i<n;i++)
		{
			s[a[i].id]=temp[i];
		}
		s[n]=0;
		build_sa(s,n+1,n+5);
		int minn=INF;
		for(i=1;i<=n;i++)
		{
			if(sa[i]>1)
				break;
		}
		int m=sa[i];
		for(i=m;i<n;i++)
		{
			printf("%d\n",num[i]);
		}
		for(i=0;i<m;i++)
		{
			s[m+i]=s[i];
		}
		
		int nn=m*2;
		s[nn]=0;
		//for(i=0;i<nn;i++)
		//	{
		//		printf("****%d\n",s[i]);
		//	}
		build_sa(s,nn+1,n+5);
		for(i=1;i<=nn;i++)
		{
			if(sa[i]>0&&sa[i]<m)
				break;
		}
		int j;
		for(j=sa[i];j<m;j++)
		{
			printf("%d\n",num[j]);
		}
		for(j=0;j<sa[i];j++)
		{
			printf("%d\n",num[j]);
		}
	//	while(1);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值