CF1063F String Journey

CF1063F String Journey


https://www.luogu.org/problemnew/show/CF1063F

太神仙了。。。

先把串反过来,那么显然要按顺序取\(k\)个长度为\(1,2,\cdots k\)的子串。

有一个简单dp做法,\(f_i\)表示到\([1,i]\)按题意能取的最多子串数量(必须满足最后一个子串结尾为\(i\)

有一个想法就是二分然后check,想一下怎么check。

假设check\(f_i=F\),那么最后一个子串是\(S[i-F+1,i]\)。如果有一个\(j\)满足\(j<i-F+1\)而且\(S[j-F+2,j]\)\(S[i-F+1,i]\)的子串,又有\(f_j\geq F-1\)那么可行。这个子串的限制就是SAM上的一个子树,直接查询子树上\(f\)最大值就行了。

有一个结论就是\(f_{i-1}\geq f_i-1\)。所以其实不用二分,\(f\)每次最多加1,每次可以\(f\)加一然后check。

check的是一个前缀,稍微想一下就可以知道 check的前缀会一直增加,所以扫一遍就行了。

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))f^=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f?x:-x;
}
int n;
int st[20][1000010];
std::vector<int>G[1000010];
int dfn[1000010],siz[1000010];
namespace SAM{
    int slink[1000010],trans[1000010][26],len[1000010],endpos[1000010],cnt,lst,leaf[1000010],flg,yes[1000010];
    struct _init{_init(){cnt=0;lst=++cnt;len[lst]=0;}}__init;
    il vd dfs(int x){
        dfn[x]=endpos[x]?++dfn[0]:dfn[0]+1;
        siz[x]=endpos[x];
        for(int i:G[x])dfs(i),siz[x]+=siz[i];
    }
    il vd extend(int ch,int i){
        int p=lst,np=++cnt;len[np]=len[p]+1;lst=np;endpos[np]=1;leaf[i]=np;
        while(p&&!trans[p][ch])trans[p][ch]=np,p=slink[p];
        if(!p)slink[np]=1;
        else{
            int q=trans[p][ch];
            if(len[q]==len[p]+1)slink[np]=q;
            else{
                int nq=++cnt;
                slink[nq]=slink[q],len[nq]=len[p]+1,memcpy(trans[nq],trans[q],sizeof trans[q]);
                while(p&&trans[p][ch]==q)trans[p][ch]=nq,p=slink[p];
                slink[np]=slink[q]=nq;
            }
        }
    }
    int t[1000010],srt[1000010];
    il vd prepare(){
        for(int i=1;i<=cnt;++i)G[slink[i]].push_back(i);
        dfs(1);
        for(int i=1;i<=cnt;++i)++t[len[i]];
        for(int i=1;i<=cnt;++i)t[i]+=t[i-1];
        for(int i=cnt;i>1;--i)srt[t[len[i]]--]=i;
        for(int i=cnt,x;i;--i)if(slink[srt[i]])x=srt[i],endpos[slink[x]]+=endpos[x];
        for(int i=1;i<=cnt;++i)st[0][i]=slink[i];
        for(int i=1;i<20;++i)
            for(int j=1;j<=cnt;++j)
                st[i][j]=st[i-1][st[i-1][j]];
    }
}
int mx[2000010];
#define mid ((l+r)>>1)
il vd update(int x,int l,int r,const int&p,const int&d){
    if(d>mx[x])mx[x]=d;if(l==r)return;
    if(p<=mid)update(x<<1,l,mid,p,d);
    else update(x<<1|1,mid+1,r,p,d);
}
il int query(int x,int l,int r,const int&L,const int&R){
    if(L<=l&&r<=R)return mx[x];
    if(L<=mid)
        if(mid<R)return std::max(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
        else return query(x<<1,l,mid,L,R);
    else return query(x<<1|1,mid+1,r,L,R);
}
#undef mid
char S[500010];
int f[500010],p;
il bool check(int i,int F){
    if(F==1)return 1;
    while(p<i-F)++p,update(1,1,n,dfn[SAM::leaf[p]],f[p]);
    int x=SAM::leaf[i-1];
    for(int j=19;~j;--j)if(st[j][x]&&SAM::len[st[j][x]]>=F-1)x=st[j][x];
    if(query(1,1,n,dfn[x],dfn[x]+siz[x]-1)>=F-1)return 1;
    x=SAM::leaf[i];
    for(int j=19;~j;--j)if(st[j][x]&&SAM::len[st[j][x]]>=F-1)x=st[j][x];
    return query(1,1,n,dfn[x],dfn[x]+siz[x]-1)>=F-1;
}
int main(){
#ifdef XZZSB
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    n=gi();scanf("%s",S+1);
    std::reverse(S+1,S+n+1);
    for(int i=1;i<=n;++i)SAM::extend(S[i]-'a',i);
    SAM::prepare();
    int ans=0;
    for(int i=1;i<=n;++i){
        f[i]=f[i-1]+1;
        while(!check(i,f[i]))--f[i];
        if(f[i]>ans)ans=f[i];
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2019-07-09 15:13  菜狗xzz  阅读(254)  评论(1编辑  收藏  举报