• 暑假 D6 T2 dict(AC自动机+DP)([JSOI2007]文本生成器)


    题目描述

    给定一个包含n个单词的字典,问有多少个长为m的串,满足至少包含字典中的一个单词,答案对10007取模,注意所有的串仅含大写字母且长度不超过100

    输入格式

    第一行两个整数m,n

    接下来n行,每行给出字典中的一个单词

    输出格式

    输出一行一个整数,表示答案

    样例输入

    2 2

    A

    B

    样例输出

    100

    数据范围

    对于100%的数据,n≤60,m≤100

    题解

    正难则反

    考虑求不经过跑到单词结尾的方案

    f[i][j]:当前在i节点,走了j步

    当i节点的儿子节点k没有end标记时,就可以向他转移

    全排列26^m,最后减去f[i][m](0≤i≤num)就是答案

    转移时不需判断是否在树上有这个边,只要没有end就转移。因为在补全Trie树时,没有这个节点就会往上面连边

    最后统计答案从0开始,一些完全不走Trie树边的答案记录在里面。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int mod=10007;
    int n,m,num;
    int go[6005][26],fail[6005];
    int f[6005][105];//当前在i节点,不经过串结尾的方案 
    bool end[6005];
    char s[105];
    
    int qpow(int a,int b){
        int ans=1;
        while(b){
            if(b&1) ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    
    void add(int len){
        int now=0;
        for(int i=0;i<len;i++){
            int c=s[i]-'A';
            if(!go[now][c]) go[now][c]=++num;
            now=go[now][c]; 
        }
        end[now]=true;
    }
    
    int q[6005],h,t;
    void get_fail(){
        h=t=0;
        for(int i=0;i<26;i++) if(go[0][i]) q[t++]=go[0][i];
        while(h<t){
            int now=q[h++];
            for(int i=0;i<26;i++){
                if(!go[now][i]) go[now][i]=go[fail[now]][i];
                else {
                    fail[go[now][i]]=go[fail[now]][i];
                    end[go[now][i]]|=end[fail[go[now][i]]];
                    q[t++]=go[now][i];
                }
            }
        }
    }
    
    int main(){
        freopen("dict.in","r",stdin);
        freopen("dict.out","w",stdout);
        scanf("%d%d",&m,&n);
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            add(strlen(s));
        }
        get_fail();
        f[0][0]=1;
        for(int i=1;i<=m;i++)
         for(int j=0;j<=num;j++)
          for(int k=0;k<26;k++)
           if(!end[go[j][k]])
            f[go[j][k]][i]=(f[go[j][k]][i]+f[j][i-1])%mod;
        int ans=qpow(26,m);
        for(int i=0;i<=num;i++)
         ans-=f[i][m];
        printf("%d",(ans%mod+mod)%mod);
    }
    View Code

     f[i][j][0/1]也可以,最后求f[i][m]的和

  • 相关阅读:
    matlab函数集锦
    视觉(1)[导入]EStereo
    我为啥申请这里的博客?
    [导入]给定一个英文原文,统计文件里面一共有多少个不同的英文单词
    视觉(2)[导入]opengl贴图程序
    C++ Builder 控件的卸载<转载>
    [转]<不知道能否解决先转下来再说>不显示删除回复显示所有回复显示星级回复显示得分回复 没有找到MSVR90D.dll因此这个应用程序未能启动
    终于把《windows程序设计》上册读完了,hoho
    乱思。。。。。。。、、、、、
    测试windows live writer
  • 原文地址:https://www.cnblogs.com/sto324/p/11191569.html
Copyright © 2020-2023  润新知