POj 3581 看起来不像后缀数组的后缀数组

先离散化数据
离散化:当数据只与它们之间的相对大小有关,而与具体是多少无关时,可以进行离散化。
思路 1:输入时反转输入,因为题目要求
2:找到最小的 i 满足 sa[i] >1 把这个后缀输出
3:翻倍剩余的字符,翻倍是因为 如 2 0 1 0 1这样的串会因为长度的问题导致字典序排序失败
4:之后对翻倍的那一段数组进行一次后缀数组
5: 求出 第一个小于第一次的边界值并且不为0的下标,之后输出都到 边界位置
6:之后把剩余的前半部分输出
7: 具体看代码

#include <stdio.h>
#include <algorithm>
#include <string.h>
#define maxs 200020
using namespace std;
struct node
{
    int id;
    int ns;
}nd[maxs];

bool nodecmp(node a,node b)
{
    if(a.ns!=b.ns)
        return a.ns<b.ns;
    return a.id<b.id;
}

int wa[maxs],wb[maxs],wv[maxs],ws[maxs];

int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}

void getsa(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb;
    for(i=0;i<m;i++) ws[i]=0;
    for(i=0;i<n;i++) ws[x[i]=r[i]]++;
    for(i=1;i<m;i++) ws[i]+=ws[i-1];
    for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=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(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i],sa[i-1],j)? p-1:p++;
    }
}

int s[maxs],sa[maxs];

int main()
{
    int n,i,j;
    scanf("%d",&n);
    for(i=n-1;i>=0;i--)
    {
        scanf("%d",&nd[i].ns);
        nd[i].id=i;
    }
    sort(nd,nd+n,nodecmp);

    for(i=0;i<n;i++)
    {
        if(i&&nd[i].ns==nd[i-1].ns)
            s[nd[i].id]=s[nd[i-1].id];
        else s[nd[i].id]=i+1;
    }
    s[n]=0;
    getsa(s,sa,n+1,n+10);
    //printf("******************\n");
    for(i=1;i<=n&&sa[i]<2;i++);
    int first=sa[i];
    for(i=first;i<n;i++)
        printf("%d\n",nd[s[i]-1].ns);
    for(i=0;i<first;i++)
        s[i+first]=s[i];
    first*=2;
    s[first]=0;
    getsa(s,sa,first+1,n+10);

    for(i=1;i<first&&(sa[i]==0||sa[i]>=first/2);i++);
    int second=sa[i];
    for(i=second;i<first/2;i++)
        printf("%d\n",nd[s[i]-1].ns);

    for(i=0;i<second;i++)
        printf("%d\n",nd[s[i]-1].ns);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值