• 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
    ", 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 }
  • 相关阅读:
    Linux的JVM可以从SUN网站上下载
    实践是最好的老师
    SCAU 8624 多项式系数累加和
    SCAU 8617 阶乘数字和 (水题)
    SCAU 8614 素数
    SCAU 8619 公约公倍
    HDU ACM 1106 排序
    Uva 465 Overflow
    SCAU 8611 大牛之路I
    SCAU 9501 ACMer不得不知道的事儿
  • 原文地址:https://www.cnblogs.com/yyf2016/p/5853873.html
Copyright © 2020-2023  润新知