POJ 1743 Musical Theme(后缀数组)

POJ 1743 Musical Theme(后缀数组)

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

题意:

        给你一个长为n的数字序列,要你求出该序列中最长的满足下面要求的序列长度:原序列中存在不重叠的两个序列,这两个序列长度相同且将第一个序列的所有数字加上某个常数可以得到第二个序列.

分析:

        本题可见罗穗骞《后缀数组——处理字符串的有力工具》.

        首先我们要求的是长度至少为5的这种不重叠序列,其实要求的这两个序列相似等价于他们两个序列中相邻元素的增幅完全一样.所以我们可以用长n的原数组的后一个数减前一个数得到长为n-1的一个新数组.

        然后在这个新数组中我们只要找最长的不重叠的重复出现2次的最长连续字串即可.

        上面这个问题就可以用后缀数组来解了.我们将上面的问题变成判定问题:是否存在一个长度为k的不重叠子序列?

        首先对新的数组求sa和height,然后将n(新数组n-1个数,但是需要加上尾0,变成n个数)个排序后的后缀分成几组,保存每组内的height值>=k.那么如果有长为k的重复不重叠子序列,那么它一定是同一组后缀的两个前缀了.如果有一组的sa最大值与sa最小值之差>=k,那么就存在这样的序列,否则k就不合适.

AC代码:注意细节(n=1的时候测试下)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=20000+1000;
struct SuffixArray
{
    int s[maxn];
    int sa[maxn],rank[maxn],height[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    int n;
    void build_sa(int m)
    {
        int i,*x=t1,*y=t2;
        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;
        for(int k=1;k<=n;k<<=1)
        {
            int 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 build_height()
    {
        int i,j,k=0;
        for(i=0;i<n;i++)rank[sa[i]]=i;
        for(i=0;i<n;i++)
        {
            if(k)k--;
            j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
}sa;
bool check(int k)
{
    int min_v=sa.sa[1],max_v=sa.sa[1];
    for(int i=2;i<sa.n;i++)
    {
        if(sa.height[i]>=k)
        {
            max_v = max(max_v,sa.sa[i]);
            min_v = min(min_v,sa.sa[i]);
            if(max_v-min_v>=k)
                return true;
        }
        else
        {
            max_v=min_v=sa.sa[i];
        }
    }
    return false;
}
int main()
{
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        int j,k;
        scanf("%d",&j);
        for(int i=0;i<n-1;i++)
        {
            scanf("%d",&k);
            sa.s[i]=k-j+100;
            j=k;
        }
        if(n<9){printf("0\n"); continue;}
        sa.n=n+1;
        sa.s[n-1]=0;
        sa.build_sa(200);
        sa.build_height();
        if(check(4)==false){printf("0\n"); continue;}
        int l=1,r=sa.n/2;
        while(l<r)
        {
            int m=l+(r-l+1)/2;
            if(check(m))
                l=m;
            else
                r=m-1;
        }
        printf("%d\n",l+1);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值