• [BZOJ1030][JSOI2007]文本生成器


    题目描述 Description

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

    输入描述 Input Description

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

    输出描述 Output Description
      一个整数,表示可能的文章总数。只需要知道结果模10007的值。
    样例输入 Sample Input
    2 2
    A
    B
    样例输出 Sample Output
    100
    数据范围及提示 Data Size & Hint
     

     肯定是一个AC自动机类题。典型的AC自动机上DP类题。

    首先把这些单词都放在一棵Trie树上,单词结尾打一下danger标记,本题的题目意思就是说在AC自动机上走M步,使得每一步都不走在danger节点的方案数。

    那么我们就开始DP 设f(i,j)表示现在在第i号点,走了j步的方案数。转移的话就比较简单了。f(i,j)可以转移到f(ch[i][k],j+1),前提是ch[i][k]不是危险节点。本题我用的是新版AC自动机,对每一个转移都一视同仁,不用管什么走Fail边。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<queue>
     5 #include<cmath>
     6 #include<cstring>
     7 using namespace std;
     8 typedef long long LL;
     9 typedef pair<int,int> PII;
    10 #define mem(a,b) memset(a,b,sizeof(a))
    11 inline int read()
    12 {
    13     int x=0,f=1;char c=getchar();
    14     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    15     while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    16     return x*f;
    17 }
    18 const int maxn=6010,MOD=10007;
    19 int n,m,tot,ch[maxn][30],Fail[maxn],danger[maxn],dp[110][maxn],ans,sum=1;
    20 bool vis[110][maxn];
    21 char s[110];
    22 int id(char c){return c-'A';}
    23 void insert(char *s)
    24 {
    25     int len=strlen(s),now=0;
    26     for(int i=0;i<len;i++)
    27     {
    28         int c=id(s[i]);
    29         if(!ch[now][c])ch[now][c]=++tot;
    30         now=ch[now][c];
    31     }
    32     danger[now]=1;
    33 }
    34 void getFail()
    35 {
    36     queue<int> Q;
    37     for(int i=0;i<26;i++)if(ch[0][i])Q.push(ch[0][i]);
    38     while(Q.size())
    39     {
    40         int u=Q.front();Q.pop();
    41         for(int i=0;i<26;i++)
    42         {
    43             int &v=ch[u][i];
    44             if(!v){v=ch[Fail[u]][i];continue;}
    45             Q.push(v);
    46             int j=Fail[u];
    47             while(j && !ch[j][i])j=Fail[j];
    48             Fail[v]=ch[j][i];
    49             danger[v]|=danger[Fail[v]];
    50         }
    51     }
    52 }
    53 int main()
    54 {
    55     n=read();m=read();
    56     for(int i=0;i<n;i++)scanf("%s",s),insert(s);
    57     getFail();
    58     dp[0][0]=1;
    59     for(int i=1;i<=m;i++)
    60         for(int j=0;j<=tot;j++)
    61             if(!danger[j])
    62                 for(int k=0;k<26;k++)
    63                 {
    64                     int &v=ch[j][k];
    65                     if(!danger[v])dp[i][v]=(dp[i][v]+dp[i-1][j])%MOD;
    66                 }
    67     for(int i=0;i<=tot;i++)if(!danger[i])ans=(ans+dp[m][i])%MOD;
    68     for(int i=1;i<=m;i++)sum=(sum*26)%MOD; 
    69     printf("%d
    ",(sum-ans+8*MOD)%MOD);
    70     return 0;
    71 }
    View Code

     

  • 相关阅读:
    计算一个未排序数组中排序后相邻元素的最大差值
    13 类对象的声明中加小括号{}和不加小括号{}的区别
    12 表中删除重复项
    11 常量区的内容不能被修改
    10 无向图的边
    顺时针旋转矩阵
    字符串的旋转
    动态规划算法
    贪心算法应用-最小生成树
    贪心算法应用-单元最短路径
  • 原文地址:https://www.cnblogs.com/FYH-SSGSS/p/6866482.html
Copyright © 2020-2023  润新知