POJ 3581 Sequence(后缀数组)
Posted forever97‘s blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ 3581 Sequence(后缀数组)相关的知识,希望对你有一定的参考价值。
【题目链接】 http://poj.org/problem?id=3581
【题目大意】
给出一个数列,将这个数列分成三段,每段分别翻转,使得其字典序最小,输出翻转后的数列。
【题解】
首先,第一个翻转点就是翻转后数列的最小后缀,注意由于一定要分成三段,则至少要剩下两个元素。难点主要是如何处理第二个翻转点,我们发现剩余的部分的每一种翻转拆分都是将两串翻转后剩余部分拼接在一起得到的串的子串,所以我们将剩余部分翻转,复制一份拼接在后面,求最小后缀即可。
【代码】
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int N=800010; int n,rank[N],sa[N],h[N],tmp[N],cnt[N],ans; int s[N]; void suffixarray(int n,int m){ int i,j,k;n++; for(i=0;i<2*n+5;i++)rank[i]=sa[i]=h[i]=tmp[i]=0; for(i=0;i<m;i++)cnt[i]=0; for(i=0;i<n;i++)cnt[rank[i]=s[i]]++; for(i=1;i<m;i++)cnt[i]+=cnt[i-1]; for(i=0;i<n;i++)sa[--cnt[rank[i]]]=i; for(k=1;k<=n;k<<=1){ for(i=0;i<n;i++){ j=sa[i]-k; if(j<0)j+=n; tmp[cnt[rank[j]]++]=j; }sa[tmp[cnt[0]=0]]=j=0; for(i=1;i<n;i++){ if(rank[tmp[i]]!=rank[tmp[i-1]]||rank[tmp[i]+k]!=rank[tmp[i-1]+k])cnt[++j]=i; sa[tmp[i]]=j; }memcpy(rank,sa,n*sizeof(int)); memcpy(sa,tmp,n*sizeof(int)); if(j>=n-1)break; }for(j=rank[h[i=k=0]=0];i<n-1;i++,k++) while(~k&&s[i]!=s[sa[j-1]+k])h[j]=k--,j=rank[sa[j]+1]; } int disc[N]; int remark(int x){ int l=1,r=n; while(l<=r){ int mid=(l+r)>>1; if(disc[mid]<x)l=mid+1; else if(disc[mid]==x)return mid; else r=mid-1; } } int a[N],p1,p2; int main(){ scanf("%d",&n); for(int i=0;i<n;i++)scanf("%d",a+i),disc[i+1]=a[i]; sort(disc+1,disc+n+1); for(int i=0;i<n;i++)a[i]=remark(a[i]); reverse_copy(a,a+n,s); suffixarray(n,n+1); for(int i=0;i<=n;i++){p1=n-sa[i];if(p1>=1&&n-p1>=2)break;} int m=n-p1; reverse_copy(a+p1,a+n,s); reverse_copy(a+p1,a+n,s+m); suffixarray(m<<1,n+1); for(int i=0;i<=2*m;i++){p2=p1+m-sa[i];if(p2>p1&&n>p2)break;} reverse(a,a+p1);reverse(a+p1,a+p2);reverse(a+p2,a+n); for(int i=0;i<n;i++)printf("%d\n",disc[a[i]]); return 0; }
以上是关于POJ 3581 Sequence(后缀数组)的主要内容,如果未能解决你的问题,请参考以下文章