zoukankan      html  css  js  c++  java
  • dtoi4680 红黑兔

    题意:

         给定一个字符串,长度小于等于500000。让你找一个二元组序列(l[i],r[i]),使得区间[l[i+1],r[i+1]]的字符串是区间[l[i],r[i]]的子串。求最长的序列长度。

    题解:

         首先有一个贪心的想法,第i个字符串肯定恰好比第i+1个字符串多一个字符。

         考虑对于位置i,如何判断一个答案mid是否可行,首先要满足的条件就是有一个位置j满足后缀j与后缀i的最长公共前缀>=mid-1,或者是后缀j与后缀i+1的最长公共前缀>=mid-1,这分别对应SA上的一段区间。还要满足的条件就是f[j]>=mid-1且j>i+mid。因此可以建立可持久化线段树,外层为字符串中的下标,内层为SA上的下标。每次就是查询一棵树中一段区间的最大值是否>=mid-1即可判断。

         这个效率是O(n*logn*logn)。其实我们发现并不需要二分,可以证明f[i]<=f[i+1]+1,因此只需要把上限设为f[i+1]+1,然后判断,不合法就减一,再判断......

         于是效率成功变成了O(nlogn)。接着我发现我忘记了后缀数组怎么写,就写了个二分hash,效率依然是两个log。

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    const int INF=1313331;
    int n,t[500002],h[500002],Min[22][500002],f[500002],ans,rt[500002],rk[500002],cnt,lgg[500002];
    unsigned long long has[500002],cf[500002],nf;
    char s[500002];
    typedef struct{
        int Max,ls,rs;
    }P;
    P p[20000002];
    bool pd(int x,int y,int l,int r){
        if (x<l)return (has[y]-has[x-1])*cf[l-x]==(has[r]-has[l-1]);
        else return (has[y]-has[x-1])==(has[r]-has[l-1])*cf[x-l];
    }
    bool cmp(const int& x,const int& y){
        int lef=0,righ=min(n-x+1,n-y+1),mid;
        while(lef<righ)
        {
            mid=(lef+righ+1)/2;
            if (pd(x,x+mid-1,y,y+mid-1))lef=mid;else righ=mid-1;
        }
        if (lef==min(n-x+1,n-y+1))return (x>y);
        return s[x+lef]<s[y+lef];
    }
    void gengxin(int r1,int r2,int begin,int end,int wz,int z){
        if (begin==end)
        {
            p[r1].Max=max(z,p[r2].Max);
            return;
        }
        int mid=(begin+end)/2;
        if (wz<=mid)
        {
            p[r1].ls=++cnt;p[r1].rs=p[r2].rs;
            gengxin(p[r1].ls,p[r2].ls,begin,mid,wz,z);
        }
        else
        {
            p[r1].rs=++cnt;p[r1].ls=p[r2].ls;
            gengxin(p[r1].rs,p[r2].rs,mid+1,end,wz,z);
        }
        p[r1].Max=max(p[p[r1].ls].Max,p[p[r1].rs].Max);
    }
    int chaxun(int root,int begin,int end,int begin2,int end2){
        if (begin>end2 || end<begin2)return 0;
        if (begin>=begin2 && end<=end2)return p[root].Max;
        int mid=(begin+end)/2;
        return max(chaxun(p[root].ls,begin,mid,begin2,end2),chaxun(p[root].rs,mid+1,end,begin2,end2));
    }
    int minh(int x,int y){
        int len=y-x+1;
        return min(Min[lgg[len]][x],Min[lgg[len]][y-(1<<lgg[len])+1]);
    }
    int ef1(int x,int y,int z){
        int lef=x,righ=y,mid;
        while(lef<righ)
        {
            mid=(lef+righ)/2;
            if (minh(mid,y-1)>=z)righ=mid;else lef=mid+1;
        }
        return lef;
    }
    int ef2(int x,int y,int z){
        int lef=x,righ=y,mid;
        while(lef<righ)
        {
            mid=(lef+righ+1)/2;
            if (minh(x,mid-1)>=z)lef=mid;else righ=mid-1;
        }
        return lef;
    }
    bool pdd(int x,int fx){
        if (fx==1)return 1;
        int l=ef1(1,rk[x],fx-1),r=ef2(rk[x],n,fx-1);
        if (chaxun(rt[x+fx],1,n,l,r)>=fx-1)return 1;
        l=ef1(1,rk[x+1],fx-1);r=ef2(rk[x+1],n,fx-1);
        if (chaxun(rt[x+fx],1,n,l,r)>=fx-1)return 1;
        return 0;
    }
    int main()
    {
        scanf("%s",s+1);n=strlen(s+1);
        nf=1;cf[0]=1;
        for (int i=1;i<=n;i++)
        {
            has[i]=has[i-1]+s[i]*nf;
            nf*=INF;cf[i]=nf;t[i]=i;
        }
        sort(t+1,t+n+1,cmp);
        for (int i=1;i<=n;i++)
        for (int j=0;(1<<j)<=i;j++)lgg[i]=j;
        for (int i=1;i<=n;i++)rk[t[i]]=i;
        for (int i=1;i<n;i++)
        {
            int x=t[i],y=t[i+1];
            int lef=0,righ=min(n-x+1,n-y+1),mid;
            while(lef<righ)
            {
                mid=(lef+righ+1)/2;
                if (pd(x,x+mid-1,y,y+mid-1))lef=mid;else righ=mid-1;
            }
            Min[0][i]=h[i]=lef;
        }
        for (int i=1;i<=20;i++)
        for (int j=1;j<=n;j++)
        Min[i][j]=min(Min[i-1][j],Min[i-1][j+(1<<i-1)]);
        for (int i=n;i>=1;i--)
        {
            f[i]=f[i+1]+1;
            while(!pdd(i,f[i]))f[i]--;
            rt[i]=++cnt;
            gengxin(rt[i],rt[i+1],1,n,rk[i],f[i]);
            ans=max(ans,f[i]);
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    观光公交
    luogu 4779 【模板】
    最小生成树(luogu 3366)
    计算系数
    更新区间,求单点—— luogu 3368
    HDU
    HDU
    HDU
    HDU
    BFS
  • 原文地址:https://www.cnblogs.com/1124828077ccj/p/12239365.html
Copyright © 2011-2022 走看看