zoukankan      html  css  js  c++  java
  • [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增

    题目链接:

    [十二省联考2019]字符串问题

    首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可。

    但即使忽略判断前缀的时间,光是连边的时间就会爆炸,显然不能暴力连边。

    对于前缀不好解决,可以将字符串翻转然后变成判断是否是后缀。

    可以发现对于后缀自动机的$parent$树,每个节点是子树内所有节点的后缀。

    那么我们可以利用$parent$树来优化建图过程,将树上每个点向子节点连边。

    对于每个$A$串和$B$串在后缀自动机上匹配出对应节点,如果是$A$串就新建节点并将$parent$树上的对应节点连向它,如果是$B$串就新建节点连向$parent$树上的对应节点。

    对于$80\%$的数据,因为保证$B$串比$A$串短,所以对于$parent$树上的一个节点既有出点又有入点是合法的($100\%$的部分最后再说)。

    但现在只解决了建图的问题,字符串在后缀自动机上匹配的时间复杂度依旧无法保证,如何找到每个$A,B$串对应的节点?

    因为$[l,r]$的串一定是$[1,r]$的串的后缀,所以$[l,r]$的串在$parent$树上对应的节点一定是$[1,r]$的串对应节点的祖先,那么我们可以记录一下所有$[1,r]$的串在$parent$树上对应的节点,然后倍增往上跳来找到$[l,r]$对应的节点。

    对于$100\%$的数据,不保证$B$串比$A$串短,那么就可能存在一个$B$串比一个$A$串长,但这两个串在$parent$树上对应的节点相同。如果像上述方式连边的话,就会导致这个$B$串能转移到$A$串。

    对于这种情况,我们将连在$parent$树上同一点的所有串存起来,按长度从小到大为第一关键字、先$B$串后$A$串为第二关键字排序,将$parent$树上每个点与父节点之间的边拆开,在这两点之间建一串点,分别与排完序的$A,B$串新建点相连,再将这一串点依次相连,这样有了上下制约关系就不会导致上述情况发生了。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<bitset>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int len[1200010];
    int pre[1200010];
    int tr[1200010][26];
    int fa[1200010][19];
    int num[200010];
    int val[1200010];
    int head[1200010];
    int to[1400010];
    int next[1400010];
    int cnt;
    int last;
    int T;
    int tot;
    char ch[200010];
    int na,nb;
    int n,m;
    int l,r;
    int x,y;
    int a[200010];
    int b[200010];
    int d[1200010];
    queue<int>q;
    int vis[1200010];
    ll f[1200010];
    int sum;
    struct lty
    {
        int len,num,opt;
        lty(){}
        lty(int LEN,int NUM,int OPT){len=LEN,num=NUM,opt=OPT;}
        bool operator <(lty a)const
        {
            return len==a.len?num>a.num:len<a.len;
        }
    };
    vector<lty>v[400010];
    inline void add(int x,int y)
    {
        next[++tot]=head[x];
        head[x]=tot;
        to[tot]=y;
        d[y]++;
    }
    inline void insert(int x,int id)
    {
        int p=last;
        int np=++cnt;
        memset(tr[np],0,sizeof(tr[np]));
        last=np;
        len[np]=len[p]+1;
        num[id]=np;
        for(;p&&!tr[p][x];p=pre[p])
        {
            tr[p][x]=np;
        }
        if(!p)
        {
            pre[np]=1;
        }
        else
        {
            int q=tr[p][x];
            if(len[q]==len[p]+1)
            {
                pre[np]=q;
            }
            else
            {
                int nq=++cnt;
                memset(tr[nq],0,sizeof(tr[nq]));
                len[nq]=len[p]+1;
                pre[nq]=pre[q];
                memcpy(tr[nq],tr[q],sizeof(tr[q]));
                pre[q]=pre[np]=nq;
                for(;p&&tr[p][x]==q;p=pre[p])
                {
                    tr[p][x]=nq;
                }
            }
        }
    }
    inline void build()
    {
        for(int i=2;i<=cnt;i++)
        {
            fa[i][0]=pre[i];
        }
        for(int j=1;j<=18;j++)
        {
            for(int i=2;i<=cnt;i++)
            {
                fa[i][j]=fa[fa[i][j-1]][j-1];
            }
        }
    }
    inline int ST(int x,int dis)
    {
        for(int i=18;i>=0;i--)
        {
            if(len[fa[x][i]]>=dis)
            {
                x=fa[x][i];
            }
        }
        return x;
    }
    inline void clear()
    {
        for(int i=1;i<=sum;i++)
        {
            v[i].clear();
        }
    }
    inline void solve()
    {
        scanf("%s",ch+1);
        n=strlen(ch+1);
        for(int i=1;i<=n;i++)
        {
            insert(ch[n-i+1]-'a',i);
        }
        build();
        sum=cnt;
        scanf("%d",&na);
        for(int i=1;i<=na;i++)
        {
            scanf("%d%d",&l,&r);
            a[i]=++cnt;
            int p=ST(num[n-l+1],r-l+1);
            v[p].push_back(lty(r-l+1,a[i],0));
            val[a[i]]=r-l+1;
        }
        scanf("%d",&nb);
        for(int i=1;i<=nb;i++)
        {
            scanf("%d%d",&l,&r);
            b[i]=++cnt;
            int p=ST(num[n-l+1],r-l+1);
            v[p].push_back(lty(r-l+1,b[i],1));
        }
        for(int i=2;i<=sum;i++)
        {
            sort(v[i].begin(),v[i].end());
            int sz=v[i].size();
            int now=pre[i];
            for(int j=0;j<sz;j++)
            {
                cnt++;
                add(now,cnt);
                if(!v[i][j].opt)
                {
                    add(cnt,v[i][j].num);
                }
                else
                {
                    add(v[i][j].num,cnt);
                }
                now=cnt;
            }
            add(now,i);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(a[x],b[y]);
        }
        for(int i=1;i<=cnt;i++)
        {
            f[i]=val[i];
            if(!d[i])
            {
                q.push(i);
                vis[i]=1;
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            for(int i=head[now];i;i=next[i])
            {
                if(f[to[i]]<f[now]+val[to[i]])
                {
                    f[to[i]]=f[now]+val[to[i]];
                }
                d[to[i]]--;
                if(!d[to[i]])
                {
                    q.push(to[i]);
                    vis[to[i]]=1;
                }
            }
        }
        ll ans=0;
        for(int i=1;i<=cnt;i++)
        {
            if(!vis[i])
            {
                printf("-1
    ");
                clear();
                return;
            }
            ans=max(ans,f[i]);
        }
        printf("%lld
    ",ans);
        clear();
    }
    inline void init()
    {
        cnt=last=1,tot=0;
        memset(head,0,sizeof(head));
        memset(to,0,sizeof(to));
        memset(val,0,sizeof(val));
        memset(num,0,sizeof(num));
        memset(pre,0,sizeof(pre));
        memset(tr[1],0,sizeof(tr[1]));
        memset(next,0,sizeof(next));
        memset(len,0,sizeof(len));
        memset(d,0,sizeof(d));
        memset(vis,0,sizeof(vis));
        memset(f,0,sizeof(f));
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            init();
            solve();
        }
    }
  • 相关阅读:
    捋一下Redis
    docker 的简单操作
    opencv的级联分类器(mac)
    python日常
    pip安装显示 is not a supported wheel on this platform.
    字节流与字符流的区别详解
    请求转发和重定向的区别及应用场景分析
    Eclipse的快捷键使用总结
    Eclipse给方法或者类添加自动注释
    IntelliJ IDEA 连接数据库 详细过程
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10676956.html
Copyright © 2011-2022 走看看