• POJ 1743 (后缀数组+不重叠最长重复子串)


    题目链接http://poj.org/problem?id=1743

    题目大意:楼教主の男人八题orz。一篇钢琴谱,每个旋律的值都在1~88以内。琴谱的某段会变调,也就是说某段的数可以加减一个旋律范围的值。问这个谱子内最长不重叠的重复部分大小。

    解题思路

    网上题解已经泛滥的题。很多细节都被先辈大神总结了。

    在当年后缀数组还不是热门的时候,这题确实是神题。

    首先对于旋律变调的处理:

    比如123,123,ans=3。

    变调之后:456,123,ans=0?不ans=3。

    所以不能使用旋律的初始值。应该取每个旋律前后的差值,这样就能保证某段无论怎么变调,都和原来一样。

    不过这样就变成n-1个旋律,并且ans会-1,可以拿笔算几组看看。

    对于取差值后的n-1个旋律,计算SA和LCP。   PS。很多人SA模板都是有问题的,并且推荐自动末尾补0的SA模板,不容易出现问题。

    要求不重叠的重复部分大小,这里套用网上被传承N遍的奇葩结论:

    将height数组分组,每组内的后缀之间的height都要大于len,如果每组内的后缀之间的最长公共前缀有大于len的而且这两个后缀的SA之差大于len就说明存在长度至少为len的不重复子串。求最长公共前缀就要用到height数组,因为这组中任意两个后缀的公共前缀必定是某些height值中的最小值,而这个值如果最大则一定是这组中height中的最大值。

    由于SA数组按字典序来的,二分SA数组长度。如果len符合要求,则先记录。再向右找更大的,否则向左。注意二分的时候ans初始值为0,不然当n=1的时候,等于没有二分就return了一个ans。

    #include "cstring"
    #include "cstdio"
    #include "string"
    #include "iostream"
    using namespace std;
    #define maxn 23000
    int n,r[maxn],tmp[maxn];
    template <class T>
    inline bool read(T &ret)
    {
        char c;
        int sgn;
        if(c=getchar(),c==EOF) return 0; //EOF
        while(c!='-'&&(c<'0'||c>'9')) c=getchar();
        sgn=(c=='-')?-1:1;
        ret=(c=='-')?0:(c-'0');
        while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
        ret*=sgn;
        return 1;
    }
    struct Suffix
    {
        int sa[maxn],rk[maxn],height[maxn];
        int t[maxn],t2[maxn],c[maxn],m;
        void init() {m=200;}
        int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];}
        void build()
        {
            int i,k,p,*x=t,*y=t2;
            r[n++]=0;
            for (i=0; i<m; i++) c[i]=0;
            for (i=0; i<n; i++) c[x[i]=r[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 (k=1,p=1; k<n; k*=2,m=p)
            {
                for (p=0,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 (i=1; i<n; i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
            }
            n--;
        }
        void LCP()
        {
            int i,j,k=0;
            for (i=1; i<=n; i++) rk[sa[i]]=i;
            for (i=0; i<n; i++)
            {
                if (k) k--;
                j=sa[rk[i]-1];
                while (r[i+k]==r[j+k]) k++;
                height[rk[i]]=k;
            }
        }
        bool judge(int len)
        {
            int l=sa[1],r=sa[1];
            for(int i=2;i<=n;i++)
            {
                if(height[i]<len)
                {
                    l=sa[i];r=sa[i];
                    continue;
                }
                l=min(l,sa[i]);
                r=max(r,sa[i]);
                if(r-l>len) return true;
            }
            return false;
        }
        int BinarySearch()
        {
            int l=0,r=n,mid,ans=0;//注意ans=0,不然当n=1的时候返回的是没赋值的ans
            while(l<=r)
            {
                int mid=l+(r-l)/2;
                if(judge(mid)) {ans=mid;l=mid+1;}
                else r=mid-1;
            }
            return ans;
        }
    };
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out1.txt","w",stdout);
        while(read(n)&&n)
        {
            for(int i=0; i<n; i++) read(tmp[i]);
            for(int i=0; i<n-1; i++) {r[i]=tmp[i+1]-tmp[i]+90;}
            Suffix a;
            a.init();
            a.build();
            a.LCP();
            int ans=a.BinarySearch();
            if(ans<4) printf("0
    ");
            else printf("%d
    ",ans+1);
        }
    }
    13560509 neopenx 1743 Accepted 840K 407MS C++ 2761B 2014-10-24 10:20:38
  • 相关阅读:
    货币系统
    纸牌
    活动
    KKT-黑白球
    POJ2676-Sudoku
    POJ1717-Dominoes
    POJ1088-滑雪
    POJ1862-Stripies
    POJ2531-Network Saboteur
    2019.12.13 数的划分
  • 原文地址:https://www.cnblogs.com/neopenx/p/4047734.html
Copyright © 2020-2023  润新知