zoukankan      html  css  js  c++  java
  • fjwc2019 D1T2 原样输出(后缀自动机+dp)

    #179. 「2019冬令营提高组」原样输出

    暴力对每个串建后缀自动机,然后暴力枚举每个自动机的子串。可以拿到部分分。

    然鹅我们可以把每个后缀自动机连起来。

    我们知道,后缀自动机是用最少的点(空间)表示出一个串的所有子串。

    那么我们为啥不在后缀自动机上直接跑dp呢?于是我们把它们连起来。

    复制一段ppt:

    可以怎么拼呢?

    我们考虑如何判断一个字符串是否是可能的输出文件

    显然我们可以贪心地匹配,每次拿起一个输入串,一直匹配到不能再加字符了才放下,这样一定是正确的

    于是我们可以告诉自动机如果失配了要往哪走

    比如有一个节点没有C的转移边,那么就要向之后第一个包含字符C的字符串的后缀自动机接受字符串C的节点连一条C的转移边

    当然如果这样的字符串不存在就不需要连边

    把自动机建起来后

    对于$k=1$的情况,直接dfs搞定

    但是面对大数据,dfs显然是会TLE的鸭

    但是仔细一看

    对于 100% 的数据,保证输入文件大小不超过 1MB ,保证输出文件大小不超过 200MB 。

    显然大数据的$k=0$,否则输出文件就超过限制了。

    于是我们在新图上跑个$dp$就行辣

    #include<cstdio>
    #include<iostream>
    using namespace std;
    #define N 10000005
    const int mod=1e9+7;
    char fr1[4]={'A','C','G','T'},s[N];
    int n,k,fr2[255],now[N],st[N],tp,ans;
    int nxt[N][4],fa[N],len[N],ed,last,rt[N],f[N];
    void dfs(int tp,int x){//k=1时用dfs找每个子串并输出
        st[tp]=x; puts(s+1); ++ans;
        for(;now[tp]<4;++now[tp]){
            if(nxt[x][now[tp]]){
                s[tp+1]=fr1[now[tp]];
                now[tp+1]=0;
                dfs(tp+1,nxt[x][now[tp]]);
                s[tp+1]=0;
            }
        }
    }
    void dp(int tp,int x){//k=0时直接dp,应对大数据
        if(f[x]>0) return;
        f[x]=1;
        for(;now[tp]<4;++now[tp]){
            if(nxt[x][now[tp]]){
                dp(tp+1,nxt[x][now[tp]]);
                now[tp+1]=0;
                f[x]=(f[x]+f[nxt[x][now[tp]]])%mod;
            }
        }
    }
    struct sam{//裸裸的后缀自动机
        void init(int x){
            last=rt[x]=++ed; char c=getchar();
            while(c<'A'||'T'<c) c=getchar();
            while(c!='
    ') add(fr2[c],x),c=getchar();
        }
        void add(int c,int x){
            int q,p=last; len[last=++ed]=len[p]+1;
            for(;p&&!nxt[p][c];p=fa[p]) nxt[p][c]=ed;
            if(!p) fa[ed]=rt[x];
            else{
                q=nxt[p][c];
                if(len[q]==len[p]+1) fa[ed]=q;
                else{
                    len[++ed]=len[p]+1;
                    for(int k=0;k<4;++k) nxt[ed][k]=nxt[q][k];
                    fa[ed]=fa[q]; fa[q]=fa[ed-1]=ed;
                    for(;nxt[p][c]==q;p=fa[p]) nxt[p][c]=ed;
                }
            }
        }
    }S;
    
    int main(){
        freopen("copy.in","r",stdin);
        freopen("copy.out","w",stdout);
        fr2['A']=0;fr2['C']=1;fr2['G']=2;fr2['T']=3;
        scanf("%d",&n);
        for(int i=1;i<=n;++i) S.init(i);
        for(int i=n-1;i;--i)
            for(int j=rt[i];j!=rt[i+1];++j)
                for(int c=0;c<4;++c)
                    if(!nxt[j][c]) nxt[j][c]=nxt[rt[i+1]][c];//贪心连边
        scanf("%d",&k);
        if(k) dfs(0,rt[1]),printf("%d",ans);
        else dp(0,rt[1]),printf("%d",f[rt[1]]);
        return 0;
    }
  • 相关阅读:
    HTML中一定会常用的标签和标签属性(这是网页构成的重要元素
    HTML(超文本语言)
    SQL SERVER中视图、事务
    important覆盖行内样式
    线性渐变
    「JavaScript面向对象编程指南」闭包
    移动端文章图片大小限制
    Vue中ajax返回的结果赋值
    「JavaScript面向对象编程指南」基础
    JS媒体查询
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10427001.html
Copyright © 2011-2022 走看看