zoukankan      html  css  js  c++  java
  • 「2019冬令营提高组」原样输出

    传送门

    考虑只有一个串的如何处理

    显然直接后缀自动机统计不同的子串个数

    考虑多个串分别构建后缀自动机

    那么对于一个可能的合法子串,它在自动机上的匹配一定是先在第一个自动机的根节点开始一直跑,只有当前自动机没有转移边了,那就跑到下一个自动机继续转移

    为了尽可能地匹配所以只有当前自动机没有转移边了,才跑到下一个自动机

    然后把所有子串分别构建一个后缀自动机,然后从后往前枚举一个个连起来,(原因代码有注释)

    所有自动机构成了一个$DAG$,考虑在$DAG$上 $dfs$

    如果要输出子串,开一个栈存当前子串,每 $dfs$ 到一个节点就输出栈内子串

    如果只考虑统计数量则记 $f[i]$ 表示跑到节点 $i$ 以后的合法子串数量,跑记忆化搜索

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=1e7+7,mo=1e9+7;
    inline int fuck(char c)//把字符按字典序转成序号,请不要在意函数名...
    {
        if(c=='A') return 0;
        if(c=='C') return 1;
        return c=='G' ? 2 : 3;
    }
    inline char FUCK(int x)//把序号转成字符
    {
        if(!x) return 'A';
        if(x==1) return 'C';
        return x==2 ? 'G' : 'T';
    }
    //后缀自动机模板
    int ch[N][4],fa[N],Max[N],tot,las,rt[N];
    inline void ins(int c,int k)
    {
        int p=++tot,f=las; Max[las=p]=Max[f]+1;
        while(f&&!ch[f][c]) ch[f][c]=p,f=fa[f];
        if(!f) { fa[p]=rt[k]; return; }
        int v=ch[f][c];
        if(Max[v]==Max[f]+1) { fa[p]=v; return; }
        int np=++tot; Max[np]=Max[f]+1;
        fa[np]=fa[v]; fa[v]=fa[p]=np;
        memcpy(ch[np],ch[v],sizeof(ch[np]));
        while(f&&ch[f][c]==v) ch[f][c]=np,f=fa[f];
    }
    
    int n,k,cnt,f[N];
    char s[N],st[N];//st存当前栈中字符
    int Top;
    void dfs1(int x)//暴力枚举所以子串
    {
        puts(st+1); ++cnt;
        for(int i=0;i<4;i++)
        {
            int &v=ch[x][i]; if(!v) continue;
            st[++Top]=FUCK(i);
            dfs1(v);
            st[Top--]=0;
        }
    }
    int dfs2(int x)//记忆化dfs统计方案
    {
        if(f[x]) return f[x];
        f[x]=1;
        for(int i=0;i<4;i++)
        {
            int &v=ch[x][i]; if(!v) continue;
            f[x]=(f[x]+dfs2(v))%mo;
        }
        return f[x];
    }
    int main()
    {
        freopen("copy.in","r",stdin);
        freopen("copy.out","w",stdout);
        n=read();
        for(int i=1;i<=n;i++)
        {
            las=rt[i]=++tot;
            scanf("%s",s+1); int len=strlen(s+1);
            for(int j=1;j<=len;j++) ins(fuck(s[j]),i);
        }
        for(int i=n;i;i--)//注意从后往前枚举
        //因为自动机i缺少的转移边自动机i+1不一定有,可能在更后面的自动机有
        //从后往前枚举可以保证 ch[rt[i+1]][k] 是后面所有自动机中第一个有 k 转移边的自动机
        {
            for(int j=rt[i];j<rt[i+1];j++)
                for(int k=0;k<4;k++) if(!ch[j][k]) ch[j][k]=ch[rt[i+1]][k];
        }
        k=read();
        if(k) dfs1(rt[1]),printf("%d",cnt);
        else dfs2(rt[1]),printf("%d",f[rt[1]]);
        return 0;
    }
  • 相关阅读:
    Element+Vue.js 选择器常用属性
    常用xml头文件
    【链接】调查显示:超20%美国大学生曾花学生贷款投
    Quartz遇到的问题
    List去重
    SpringDataJPA
    IDEA:Error during artifact deployment. See server log for details.详解
    Quartz定时任务
    多线程条件通行工具——CountDownLatch
    多线程同步工具——Lock
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10486598.html
Copyright © 2011-2022 走看看