POJ3581 Sequence(后缀数组坑题)

题意:将一个长度为N(<=200000)的数组,划分成非空的三段,然后倒转每一段拼成一个新的数组(三段相对位置不变),输出字典序最小的结果。

 

思路:要求的就是倒转后字典序最小的结果,先倒转原数组,跑一次SA,之后后缀会按照字典序排列,我们只需要找到第一个满足sa[i]>1(因为要保证后面两段非空)的位置就行了,这就是第一段。

去掉第一段后,我们需要找出剩下的部分中字典序较小的部分作为第二段,直接找是不行的,参考下面两组数据:

9
8 4 -1 5 0 5 0 2 3
8
9 0 2 1 2 1 5 6

在输出-1 4 8后会认为0 5的字典序比0505小,会输出-1 4 8 0 5 3 2 0 5,然后实际上结果应该是-1 4 8 0 5 0 5 3 2.

为了解决这个问题,我们将剩下的数组复制一次接在原来的后面,再跑一次SA,这样再找就能保证找出的第二段会使结果字典序最小了,所以数组要开2倍。

题目中没有给数据范围,要先离散化到1-N。

此题最坑的地方就是多组case交C++会WA,只能交G++!!!

WA了两天才在discuss找到原因,一度自闭。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <cstdlib>
#include <set>
#include <map>
#include <vector>
#include <string>

using namespace std;

typedef long long ll;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn = 400005;
const int mod = 1000000007;

int s[maxn];
int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];
// 传入参数:str,sa,len+1,ASCII_MAX+1
void da(const int r[],int sa[],int n,int m) {
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++) Ws[i]=0;
    for(i=0; i<n; i++) Ws[x[i]=r[i]]++;//以字符的ascii码为下标
    for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
    for(i=n-1; i>=0; i--) sa[--Ws[x[i]]]=i;
    //for(int i=0;i<n+1;i++)cout<<sa[i]<<' ';
    for(j=1,p=1; p<n; j*=2,m=p) {
        for(p=0,i=n-j; i<n; i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<n; i++) wv[i]=x[y[i]];
        for(i=0; i<m; i++) Ws[i]=0;
        for(i=0; i<n; i++) Ws[wv[i]]++;
        for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
        for(i=n-1; i>=0; i--) sa[--Ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
            //x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
}
int sa[maxn];
struct node {
    int a, id, idx;
}p[maxn];
bool c1(struct node a, struct node b) {
    return a.a < b.a;
}
bool c2(struct node a, struct node b) {
    return a.idx > b.idx;
}
int b[maxn];
int main() {
    int n;
    //while (~scanf("%d", &n)) {
    scanf("%d", &n);
        for (int i = 0; i < n; ++i) {
            scanf("%d", &p[i].a);
            p[i].idx = i;
            s[i] = b[i] = p[i].a;
        }
        int mx = 1;
        // 离散化
        sort(p, p + n, c1);
        for (int i = 0; i < n; ++i) {
            p[i].id = (i == 0 ? 1 : (p[i].a == p[i - 1].a ? p[i - 1].id : p[i - 1].id + 1));
            mx = max(mx, p[i].id);
        }
        sort(p, p + n, c2);
        for (int i = 0; i < n; ++i) {
            s[i] = p[i].id;
        }

        s[n] = 0;
        da(s, sa, n + 1, mx + 2);
        int pos, len;
        for (int i = 1; i <= n; ++i) {
            if (sa[i] > 1) {
                len = pos = sa[i];
                //printf("1 i=%d sa=%d\n", i, sa[i]);
                // 第一段
                for (int j = pos; j < n; ++j) {
                    printf("%d\n", p[j].a);
                }
                break;
            }
        }
        // 复制剩下的数组
        for (int i = 0; i < pos; ++i) {
            s[i + pos] = s[i];
        }
        len <<= 1;
        s[len] = 0;
        da(s, sa, len + 1, mx + 2);
        for (int i = 1; i <= len; ++i) {
            if (sa[i] > 0 && sa[i] < pos) { 
                //printf("2 i=%d sa=%d\n", i, sa[i]);
                // 第二段
                for (int j = sa[i]; j < pos; ++j) {
                    printf("%d\n", p[j].a);
                }
                // 第三段
                for (int j = 0; j < sa[i]; ++j) {
                    printf("%d\n", p[j].a);
                }
                break;
            }
        }
    //}
    return 0;
}
/*
5
10 1 2 3 4
4
3 2 1 2
5
9 7 3 9 1
9
8 4 -1 5 0 5 0 2 3
8
9 0 1 2 1 2 5 6
3
3 2 1
5
1 2 3 2 1
*/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值