首页 > 代码库 > poj 3581 Sequence(后缀数组,离散化)详解

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\n", 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 }

 

poj 3581 Sequence(后缀数组,离散化)详解