POJ3581 后缀数组

http://poj.org/problem?id=3581

这题说是给了N个数字组成的序列A1 A2 ..An 其中A1 大于其他的数字 , 现在要把序列分成三段并将每段分别反转求最小字典序

以后还是老老实实用基数排序 用sort wa 了 一晚

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
#include <vector>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
const int maxn =200005;
struct SuffixArry
{
    int c[maxn*2],t[maxn*2],t1[maxn*2],sa[maxn*2];
    void build(int n,int m,int *S)
    {
         int *x=t,*y=t1,i;
         for( i=0; i<m; i++)c[i]=0;
         for( i=0; i<n; i++)c[x[i]=S[i] ]++;
         for( i=1; i<m; i++)c[i]+=c[i-1];
         for( i=n-1; i>=0; i--)sa[ --c[ x[i] ] ]=i;
         int p;
         for(int k=1; k<=n; k<<=1 )
         {
            p=0;
            for( i = n-k; i<n; i++)y[p++]=i;
            for( i=0; i<n; i++) if(sa[i]>=k)y[p++]=sa[i]-k;
            for( i=0; i<m; i++)c[ i ] =0 ;
            for( i=0 ; i<n; i++ )c[ x[ y[i] ] ]++;
            for( i=1; i<m; i++) c[i]+=c[i-1];
            for( i=n-1; i>=0; i--) sa[--c[ x[ y[i] ] ] ]=y[i];
            swap(x,y);
            p=1; x[ sa[ 0 ] ] = 0;
            for(int i=1; i<n; i++)
             x[ sa[i] ] = y[ sa[i] ]== y[ sa[i-1] ]&& y[ sa[i]+k ]==y[sa[i-1]+k]?p-1:p++;
            if(p>=n)break;
            m=p;
         }
    }
    void clear(){
        memset(t,-1,sizeof(t));
        memset(t1,-1,sizeof(t1));
    }
}T;
int A[maxn],B[maxn],C[maxn],rev[maxn*2];
void revercopy(int *a, int *b, int len)
{
     for(int i=0; i<len; i++)
     {
          b[i]=a[len-1-i];
     }
}
void rever(int *a, int len)
{
      for(int i=0; i<len/2; i++ )
      {
           int t= a[i];
           a[i]=a[len-1-i];
           a[len-1-i]=t;
      }
}
int main(int argc, char *argv[]) {

     int n;
     scanf("%d",&n);
     for(int i=0; i<n; i++)
        {
          scanf("%d",&A[i]);
          B[i]=A[i];
        }
     sort(B,B+n);
     int L=unique(B,B+n)-B;
     for(int i=0; i<n; i++)
         {
            C[i]=lower_bound(B,B+L,A[i])-B;
         }
     int p1;
     T.clear();
     revercopy(C,rev,n);
     T.build(n,L,rev);

      for(int i=0; i<n; i++)
      {
           p1 = n-T.sa[i];
           if(p1>0&&n-p1>=2)break;
      }

      if(p1<1||n-p1<2)while(true){};
      int m= n-p1;
      revercopy(C+p1,rev,m); //for(int i=0; i<m; i++)printf("%d ",rev[i]);
      revercopy(C+p1,rev+m,m);//for(int i=0; i<m*2; i++)printf("%d ",rev[i]);
      T.build(m*2,L,rev);
      int p2;
      for(int i=0; i<m*2; i++)
      {
          p2 = p1 + m - T.sa[ i ];
          if(p2>p1&&n-p2>=1)break;
      }
      if(p2<=p1||n-p2<1)while(true){
      };
      rever(A,p1);
      rever(A+p1,p2-p1);
      rever(A+p2,n-p2);
      for(int i=0; i<n; i++) printf("%d\n",A[i]);
    return 0;
}

时间: 2024-08-24 11:04:19

POJ3581 后缀数组的相关文章

poj3581 后缀数组的应用

题意是给你n个数字组成的序列将序列切成三段, 然后将三段逆序之后字典序最小, 问你该怎么切,保证序列中第一个数最大, 我们可以找到两个切点然后翻转即可, 第一个切点可以将整个序列翻转之后找出最小的后缀即可, 对于第二个假设我们将ABCD序列中AB CD分别翻转得到BADC字典序最小, 那么我们可以构造一个序列DCBADCBA按照第一种处理即可得到第二个切点,  代码如下: #include <algorithm> #include <cstring> #include <cs

POJ3581 Sequence(后缀数组)

题意:给一个串,串的第一个字符比后面的都大,要把它分成三段,然后反转每一段,求能得到的字典序最小的串是什么. 首先,第一段是可以确定的:把原串反转,因为第一个字符是最大的,它是唯一的,不存在反转串的后缀之间有包含关系,所以取最小的后缀这就是第一段的字符串: 然后后面两段,如果确定分割位置可以发现这两段字符串构成是一个从分割位置出发逆时针循环回来的串—— 即接下来要求的就是剩余部分的反转的最小表示,可以用后缀数组来做:把串加长一倍,答案就在最小的且长度大于等于原串长度的后缀了. 注意,分的段要非空

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 guaranteed A1 > A2, ..., An,  you are to cut it into three sub-sequences and

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

hdu5769--Substring(后缀数组)

题意:求含有某个字母的某个字符串的不同子串的个数 题解:后缀数组,记录每个位置距离需要出现的字母的距离就可以了.因为不太了解后缀模版卡了一会,还是很简单的. 记住sa和height数组都是1-n的下标. //后缀数组 #include <stdio.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll;

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

【tyvj1860】后缀数组

描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串.后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0你的任务就是求出SA和height这两个数组.字符串长度<=200000 输入格式 一行,为描述中的字符串(仅会出现小写

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the