CF1063F String Journey

http://codeforces.com/problemset/problem/1063/F

题解

首先我们注意到对于任意一种划分,我们都可以把它调整长度为\((len,len-1,len-2....1)\)的。所以我们可以用\((\tt{第一个串的位置,第一个串的长度})\)来表示一个划分。

所以我们可以设\(dp[i][j]\)表示\((i,j)\)这种方案是否可行,转移是从后往前的,可以用哈希,因为第二维是根号级别的,所以复杂度为\(n\sqrt{n}\)

发现若有\(dp[i][j]\),那么就有\(dp[i][j-1]\),我们可以换一种状态设计令\(dp[i]\)表示最大的合法的\(j\)

若要判断\((i,j)\)是否合法,我们需要在\((i+j,n)\)中找到\(lcp(i,x)\geq j-1 ||lcp(i+1,x)\geq j-1\),这个可以用后缀数组+主席树在\(logn\)的时间内判断。

这个东西可以二分,但是我们发现\(dp[i]\leq dp[i+1]+1\),于是我们的判断次数就变成\(O(n)\)的了。

代码

#include<bits/stdc++.h>
#define N 500009
#define inf 1e9
#define ls tr[cnt].l
#define rs tr[cnt].r
using namespace std;
typedef long long ll;
char s[N];
int n,tong[N],y[N],rnk[N],sa[N],dp[N],p[21][N],h[N],m,Log[N],tott,T[N];
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct seg{
    int l,r,mx;
}tr[N*21];
int query(int cnt,int l,int r,int L,int R){
    if(!cnt)return 0;
    if(l>=L&&r<=R)return tr[cnt].mx;
    int mid=(l+r)>>1,ans=0;
    if(mid>=L)ans=max(ans,query(ls,l,mid,L,R));
    if(mid<R)ans=max(ans,query(rs,mid+1,r,L,R));
    return ans;
}
void upd(int &cnt,int pre,int l,int r,int x,int y){
    cnt=++tott;tr[cnt]=tr[pre];
    if(l==r){
      tr[cnt].mx=y;
      return;
    }
    int mid=(l+r)>>1;
    if(mid>=x)upd(ls,tr[pre].l,l,mid,x,y);
    else upd(rs,tr[pre].r,mid+1,r,x,y);
    tr[cnt].mx=max(tr[ls].mx,tr[rs].mx);
}
inline int lcp(int l,int r){
    if(l>r)swap(l,r);
    if(l==r)return inf;
    l++;
    int lo=Log[r-l+1];
    return min(p[lo][l],p[lo][r-(1<<lo)+1]);
}
inline void qsort(){
    for(int i=0;i<=m;++i)tong[i]=0;
    for(int i=1;i<=n;++i)tong[rnk[i]]++;
    for(int i=1;i<=m;++i)tong[i]+=tong[i-1];
    for(int i=n;i>=1;--i)sa[tong[rnk[y[i]]]--]=y[i];
}
inline void SA(){
    m=200;
    for(int i=1;i<=n;++i)rnk[i]=s[i],y[i]=i;
    qsort();
    for(int w=1,p=0;p<n;w<<=1,m=p){
        p=0;
        for(int i=n-w+1;i<=n;++i)y[++p]=i;
        for(int i=1;i<=n;++i)if(sa[i]>w)y[++p]=sa[i]-w;
        qsort();
        swap(rnk,y);
        rnk[sa[1]]=p=1;
        for(int i=2;i<=n;++i)
            rnk[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w])?p:++p;
    }
    for(int i=1;i<=n;++i){
        if(rnk[i]==1)continue;
        int y=max(0,h[rnk[i-1]]-1);
        while(s[i+y]==s[sa[rnk[i]-1]+y])y++;
        h[rnk[i]]=p[0][rnk[i]]=y;
    }
    for(int i=1;(1<<i)<=n;++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            p[i][j]=min(p[i-1][j],p[i-1][j+(1<<i-1)]);
}
inline bool pd(int x,int len){
    int l=rnk[x],r=n,ansr=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(lcp(rnk[x],mid)>=len-1)ansr=mid,l=mid+1;
        else r=mid-1;
    }
    l=1;r=rnk[x];
    int ansl=n+1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(lcp(mid,rnk[x])>=len-1)ansl=mid,r=mid-1;
        else l=mid+1;
    }
    if(ansl<=ansr)if(query(T[x+len],1,n,ansl,ansr)>=len-1)return 1;
    x++;
    l=rnk[x],r=n,ansr=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(lcp(rnk[x],mid)>=len-1)ansr=mid,l=mid+1;
        else r=mid-1;
    }
    l=1;r=rnk[x];
    ansl=n+1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(lcp(mid,rnk[x])>=len-1)ansl=mid,r=mid-1;
        else l=mid+1;
    }
    if(ansl<=ansr)if(query(T[x+len-1],1,n,ansl,ansr)>=len-1)return 1;
    return 0;
}
int main(){
    n=rd();
    scanf("%s",s+1);
    for(int i=2;i<=n;++i)Log[i]=Log[i>>1]+1;
    SA();
    int ans=0;
    for(int i=n;i>=1;--i){
       int mx=dp[i+1]+1;
       while(!pd(i,mx))mx--;
       dp[i]=mx;
       upd(T[i],T[i+1],1,n,rnk[i],mx);
       ans=max(ans,dp[i]);
    }
    cout<<ans;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ZH-comld/p/11001681.html
今日推荐