[置顶] poj 3581 Sequence (后缀数组好题)

题意:

给出一个字符串,求这个字符串分成三段并且反转着三段得到字典序最小的序列。题目保证第一个字符最大。

题解:

题目保证第一个字符最大是非常有用的条件,我们可以用贪心,将字符串倒叙,找到字典序最小的后缀作为第一段,注意最小要字典序是在不会导致有一段是空串的前提下,例如 4 3 2 1 明显倒序后最小后缀时1 2 3,但是这样不可取,因为如果去1 2 3,那么其他两段就是空串了!这是求出了第一段,第二段不能在刚才串的基础上用最小后缀,因为会有这样的数据6 10 1 2 2 3 4 ,这个数据经过第一步之后变成4 3 2 2,字典序最小的后缀时2,显然是错的。一个处理的方法就是将原串复制一份到原串的结尾,然后进行后缀数据,找到原串范围内的最小后缀,前提也是要保证不出现空串。第三步直接将剩余串输出!


#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const int MOD=10007;
const int maxn=400005;
int rank[maxn],SA[maxn],height[maxn];
int t1[maxn],t2[maxn],t3[maxn],t4[maxn];
int ss[maxn];

void Swap(int*& x,int*& y){

    int *temp=x;
    x=y;
    y=temp;
}

bool cmp(int t[],int a,int b,int l){

    return t[a]==t[b]&&t[a+l]==t[b+l];
}

bool cp(int a,int b){

    return ss[a]<ss[b];
}

void build_SA(int s[],int len,int up){

    int *k1=t1,*k2=t2,*r=t3,*cnt=t4;
    for(int i=0;i<len;i++)k2[i]=i;
    sort(k2,k2+len,cp);
    for(int i=0;i<len;i++)SA[i]=k2[i];
    k1[SA[0]]=0;
    int p=1;
    for(int i=1;i<len;i++){
        k1[SA[i]]= s[SA[i]]==s[SA[i-1]] ? p-1 : p++;
    }
    up=p;
    p=1;
    for(int d=1;p<len;d<<=1,up=p){

        p=0;
        for(int i=len-d;i<len;i++)k2[p++]=i;
        for(int i=0;i<len;i++)if(SA[i]>=d)k2[p++]=SA[i]-d;
        for(int i=0;i<len;i++)r[i]=k1[k2[i]];

        for(int i=0;i<up;i++)cnt[i]=0;
        for(int i=0;i<len;i++)cnt[r[i]]++;
        for(int i=1;i<up;i++)cnt[i]+=cnt[i-1];
        for(int i=len-1;i>=0;i--)SA[--cnt[r[i]]]=k2[i];

        Swap(k1,k2);
        k1[SA[0]]=0;
        p=1;
        for(int i=1;i<len;i++){
            k1[SA[i]]= cmp(k2,SA[i-1],SA[i],d) ? p-1 : p++;
        }
    }
}

void get_height(int s[],int len){

    for(int i=1;i<=len;i++)rank[SA[i]]=i;
    for(int i=0,p=0;i<len;i++){

        int j=SA[rank[i]-1];
        while(s[i+p]==s[j+p])p++;
        height[rank[i]]=p;
        if(p)p--;
    }
}

void output(int out[],int s,int e){

    for(int i=s;i<=e;i++){
        printf("%d\n",out[i]);
    }
}

int main(){

    int n,e,start;
    scanf("%d",&n);
    for(int i=n-1;i>=0;i--)scanf("%d",&ss[i]);
    ss[n]=-oo;
    build_SA(ss,n+1,-1);
    get_height(ss,n);
    for(int i=1;i<=n;i++){
        if(SA[i]>=2){
            start=SA[i];
            break;
        }
    }
    output(ss,start,n-1);
    e=start;
    n=e;
    for(int i=0;i<e;i++)ss[n++]=ss[i];
    ss[n]=-oo;
    build_SA(ss,n+1,-1);
    get_height(ss,n);
    for(int i=1;i<=n;i++){
        if(SA[i]<e&&SA[i]>=1){
            start=SA[i];
            break;
        }
    }
    output(ss,start,e-1);
    output(ss,0,start-1);
    return 0;
}
/**
4
10000
2
1000
6

3
3 2 1
*/



你可能感兴趣的:([置顶] poj 3581 Sequence (后缀数组好题))