您的位置:首页 > 产品设计 > UI/UE

poj3581 Sequence(后缀数组sa的运用+离散化)

2015-09-21 17:30 447 查看
题目链接:点击打开链接

题意描述:给定一个整数序列,序列的第一个元素大于其余所有的元素,先把序列截成三段,然后对每一段进行逆转,求新生成的序列中字典序最小的序列?

解题思路:后缀数组sa的运用+离散化

分析:

先分析第一段:



如图所示表示目标序列,对第一段的长度进行讨论,其中红色1表示第一个元素,也就是最大值

1、如果上面三个序列在0~1区间不等,那么我们选取字典序较小的即可,否则我们在第1和第2个之间选取

2、如果前两个序列在1~2区间上不等,那么我们选取字典序较小的即可,否则我们选取第一个

从上述讨论我们发现对于第一段的选取我们需要选出逆序后字典序最小的即可采用后缀数组可以很容易解决

对于第二段和第三段的选取,我们可以把剩下的序列逆序重复两遍之后找字典序最小的即可

如x1~x2y1~y2z1~z2其中x1~x2已经确定,如果使剩下的最小呢?即:y2~y1z2~z1

我们只需要构造出这样的序列即可:z2~z1y2~y1z2~z1y2~y1

使用后缀数组sa可以很容易解决这个问题

代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 400010;
int cmp(int *r,int a,int b,int l)
{
return (r[a]==r[b]) && (r[a+l]==r[b+l]);
}
int wa
,wb
,ww
,wv
;
void DA(int *r,int *sa,int n,int m)  //此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0; i<m; i++) ww[i]=0;
for(i=0; i<n; i++) ww[x[i]=r[i]]++;
for(i=1; i<m; i++) ww[i]+=ww[i-1];
for(i=n-1; i>=0; i--) sa[--ww[x[i]]]=i; //预处理长度为1
for(j=1,p=1; p<n; j*=2,m=p) //通过已经求出的长度J的SA,来求2*J的SA
{
for(p=0,i=n-j; i<n; i++) y[p++]=i; // 特殊处理没有第二关键字的
for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j; //利用长度J的,按第二关键字排序
for(i=0; i<n; i++) wv[i]=x[y[i]];
for(i=0; i<m; i++) ww[i]=0;
for(i=0; i<n; i++) ww[wv[i]]++;
for(i=1; i<m; i++) ww[i]+=ww[i-1];
for(i=n-1; i>=0; i--) sa[--ww[wv[i]]]=y[i]; //基数排序部分
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;  //更新名次数组x[],注意判定相同的
}
}
struct node
{
int v,rk,id;
} st
;
bool cmp1(node a,node b)
{
return a.v<b.v;
}
bool cmp2(node a,node b)
{
return a.id<b.id;
}
int n,str
,sa
,ans
;
int main()
{
scanf("%d",&n);
for(int i=0; i<n; ++i){
scanf("%d",&st[i].v);st[i].id=i;
}
sort(st,st+n,cmp1);
int rk=1;
st[0].rk=rk++;
ans[st[0].rk]=st[0].v;
for(int i=1; i<n; ++i){
if(st[i].v==st[i-1].v) st[i].rk=st[i-1].rk;
else{ st[i].rk=rk++;ans[st[i].rk]=st[i].v; }
}
sort(st,st+n,cmp2);
for(int i=0; i<n-2; ++i)
str[i]=st[n-2-i-1].rk;
str[n-2]=0;
DA(str,sa,n-1,rk);
int len1=sa[1];
for(int i=len1; i<n-2; ++i)
printf("%d\n",ans[str[i]]);
int m=len1+2;
for(int i=0; i<m; i++)
str[m-i-1]=str[2*m-i-1]=st[i+n-m].rk;
n=m;
str[2*n]=0;
DA(str,sa,2*n+1,rk);///2*n+1
int x;
for(int i=1; i<=2*n; ++i)///问题1
if(sa[i]>0&&sa[i]<n){ x=sa[i];break;}
for(int i=0; i<n; ++i) printf("%d\n",ans[str[x+i]]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: