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\n",ans);
    return 0;
}
上一篇:如何在低代码平台上定义数据处理逻辑?微软Power-Fx选择了Excel公式


下一篇:Zepto.js