zoukankan      html  css  js  c++  java
  • [JSOI2007]文本生成器

    1030: [JSOI2007]文本生成器

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 4376  Solved: 1794
    [Submit][Status][Discuss]

    Description

      JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,
    他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文
    章—— 也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,
    那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的
    标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的?。ZYX需要指出GW文本生成器 v6
    生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗?

    Input

      输入文件的第一行包含两个正整数,分别是使用者了解的单词总数N (<= 60),GW文本生成器 v6生成的文本固
    定长度M;以下N行,每一行包含一个使用者了解的单词。这里所有单词及文本的长度不会超过100,并且只可能包
    含英文大写字母A..Z

    Output

      一个整数,表示可能的文章总数。只需要知道结果模10007的值。

    Sample Input

    2 2
    A
    B

    Sample Output

    100

    HINT

     

    Source

    AC自动机+dp

    依次读入所以单词并把它们丢到trie树中,把每个单词的结尾标记一下,然后建立fail指针,在建立时需要注意如果某个点的fail有结尾标记,那么当前点也要相应的标记一下。

    正难则反。答案就等于所有26^m-不合法的数量。

    我们求出不合法的个数然后用可能生成的总文本个数减去不合法的个数即为答案,那么不合法的个数怎么求呢,我们在trie树的一些节点上打了标记,如果我们在构造文本的时候选用了某个带标记的节点,那么当前文本就合法了,我们要求不合法的,就需要避开打过标记的节点,用没有标记的节点去转移。

    f[i][j] 表示文本的第i位,匹配到trie 树中的第j 个节点

    状态转移方程: f[i][ch[k][j]]=(f[i][ch[k][j]]+f[i-1][k])%p (ch[k][j]表示节点k的儿子) 转移时必须保证k 没有标记。

    #include<cstdio>
    #include<cstring>
    #define Sz 26
    #define mod 10007
    using namespace std;
    const int N=105,M=1e4+5,Z=26;
    int n,m,cnt=1,tr[M][Z],fail[M],tag[M],q[M];
    int ans1,ans2=1,f[N][M];char s[N];
    void insert(){
        scanf("%s",s);
        int now=1,l=strlen(s);
        for(int z,i=0;i<l;i++){
            z=s[i]-'A';
            if(!tr[now][z]) tr[now][z]=++cnt;
            now=tr[now][z];
        }
        tag[now]++;
    }
    void acmach(){
        for(int i=0;i<Sz;i++) tr[0][i]=1;
        int h=0,t=1,now,p;q[t]=1;
        while(h!=t){
            now=q[++h];
            for(int i=0;i<Sz;i++){
                if(!tr[now][i]) continue;
                p=fail[now];
                while(!tr[p][i]) p=fail[p];
                p=tr[p][i];
                if(tag[p]) tag[tr[now][i]]++;
                fail[tr[now][i]]=p;
                q[++t]=tr[now][i];
            }    
        }
    }
    void dp(int id){
        for(int i=1,p;i<=cnt;i++){
            if(tag[i]||!f[id-1][i]) continue;
            for(int j=0;j<Sz;j++){
                p=i;
                while(!tr[p][j]) p=fail[p];
                p=tr[p][j];
                f[id][p]=(f[id][p]+f[id-1][i])%mod;
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) insert();
        acmach();
        f[0][1]=1;
        for(int i=1;i<=m;i++) dp(i);
        for(int i=1;i<=cnt;i++) if(!tag[i]) ans1=(ans1+f[m][i])%mod;
        for(int i=1;i<=m;i++) ans2=(ans2*26)%mod;
        printf("%d
    ",(ans2-ans1+mod)%mod);
        return 0;
    }
  • 相关阅读:
    聚类算法
    shell编程之运算符
    linux shell编程子bash变量
    java SSM框架单元测试最佳实战代码
    在maven项目中使用Junit进行单元测试(一)
    JMeter之Http协议接口性能测试
    TestNG学习笔记新的
    单元测试最佳实战项目代码下载
    单元测试中使用mock最好不要使用easymock而应该使用powermock
    springboot 读写excel
  • 原文地址:https://www.cnblogs.com/shenben/p/6549830.html
Copyright © 2011-2022 走看看