zoukankan      html  css  js  c++  java
  • 题解 CF1063F 【String Journey】

    首先将字符串翻转,相应地,有序串组也就翻转了,即为求最长的长度递增的有序串组,满足第 (i) 个串是第 (i+1) 个串的子串。进行贪心,发现最优秀情况下有序串组中的串长一定是逐渐加 (1) 的,即长度为 (1,2,3,dots),因为可以对不是逐渐加 (1) 的位置删去一些字符,来使之后能匹配更多,所以这样是最优的。

    考虑 (DP),设 (dp_i) 为最后一个字符串在 (i) 位置结束的最长有序串组的长度。有序串组的长度是具有单调性的,可以二分。二分长度 (lenth),因为串长是逐渐加 (1) 的,所以上一个串一定是当前串删去开头字符或删去结尾字符形成的,那么就是找 (s[i-lenth+1,i])(s[i-lenth,i-1]) 在之前的出现位置,根据结尾位置的 (dp) 值是否大于等于 (dp_{i}-1) 判定即可。出现位置可以通过在 (SAM)(Parent) 树上查询子树,用线段树来进行维护最大值。

    发现有 (dp_i -1 leqslant dp_{i-1}),因为一定存在一种 (dp_{i-1}) 的方案可以用 (dp_i) 的方案里每个串删去一个字符来表示出来,所以可以不用二分,直接枚举即可,(dp_i) 每次最多加 (1),得枚举复杂度为 (O(n))。发现 (i-lenth) 是单调递增的,所以可以用一个指针来维护判定的范围。

    #include<bits/stdc++.h>
    #define maxn 1000010
    #define maxm 4000010
    #define ls (cur<<1)
    #define rs (cur<<1|1)
    #define mid ((l+r)>>1)
    using namespace std;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int n,ans,tot=1,las=1,root=1,cnt,pos;
    int fa[maxn],len[maxn],ch[maxn][26],bel[maxn],dp[maxn];
    int in[maxn],out[maxn],f[maxn][22],mx[maxm];
    char s[maxn];
    struct edge
    {
        int to,nxt;
    }e[maxn];
    int head[maxn],edge_cnt;
    void add(int from,int to)
    {
        e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
    }
    void modify(int l,int r,int pos,int v,int cur)
    {
        if(l==r)
        {
            mx[cur]=v;
            return;
        }
        if(pos<=mid) modify(l,mid,pos,v,ls);
        else modify(mid+1,r,pos,v,rs);
        mx[cur]=max(mx[ls],mx[rs]);
    }
    int query(int L,int R,int l,int r,int cur)
    {
        if(!L||!R) return 0;
        if(L<=l&&R>=r) return mx[cur];
        int v=0;
        if(L<=mid) v=max(v,query(L,R,l,mid,ls));
        if(R>mid) v=max(v,query(L,R,mid+1,r,rs));
        return v;
    }
    void insert(int c,int id)
    {
        int p=las,np=las=++tot;
        len[np]=len[p]+1,bel[id]=np;
        while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
        if(!p) fa[np]=root;
        else
        {
            int q=ch[p][c];
            if(len[q]==len[p]+1) fa[np]=q;
            else
            {
                int nq=++tot;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                len[nq]=len[p]+1,fa[nq]=fa[q],fa[np]=fa[q]=nq;
                while(ch[p][c]==q) ch[p][c]=nq,p=fa[p];
            }
        }
    }
    void dfs(int x)
    {
        in[x]=++cnt,f[x][0]=fa[x];
        for(int i=1;i<=20;++i) f[x][i]=f[f[x][i-1]][i-1];
        for(int i=head[x];i;i=e[i].nxt) dfs(e[i].to);
        out[x]=cnt;
    }
    int get(int x,int lenth)
    {
        x=bel[x];
        for(int i=20;i>=0;--i)
            if(f[x][i]&&len[f[x][i]]>=lenth)
                x=f[x][i];
        return x;
    }
    bool check(int p)
    {
        int x=get(p,dp[p]-1),y=get(p-1,dp[p]-1);
        return max(query(in[x],out[x],1,tot,root),query(in[y],out[y],1,tot,root))>=dp[p]-1;
    }
    int main()
    {
        read(n),scanf("%s",s+1),reverse(s+1,s+n+1);
        for(int i=1;i<=n;++i) insert(s[i]-'a',i);
        for(int i=2;i<=tot;++i) add(fa[i],i);
        dfs(root);
        for(int i=1;i<=n;++i)
        {
            dp[i]=dp[i-1]+1;
            while(!check(i))
                dp[i]--,pos++,modify(1,tot,in[bel[pos]],dp[pos],root);
            ans=max(ans,dp[i]);
        }
        printf("%d",ans);
        return 0;
    }
    
  • 相关阅读:
    委托系列整理
    EF Lambda 多表查询
    枚举,Enum,常规使用demo记录
    自定义Window 服务
    xpath 操作XML
    MVC 自定义过滤器
    时间比对,常用细节记录
    Lock锁_线程_线程域
    break、continue和goto 三者作用介绍
    .net 学习路线感想
  • 原文地址:https://www.cnblogs.com/lhm-/p/13533529.html
Copyright © 2011-2022 走看看