bzoj1030 文本生成器

来源:互联网 发布:淘宝新手怎么开直通车 编辑:程序博客网 时间:2024/04/29 11:30

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的值。

此题其实和bzoj2938 病毒(点这里)有异曲同工之处。
问题可以转化为建立一个长度为m的字符串,使之不包含任何一个序列有多少种情况。再用总排列减去即可。
还是建立AC自动机以后标记危险结点【详见这里】,然后dp。dp[i][j]表示走i步到j结点的方案数。
因为不记父亲只记儿子,所以用刷表法比较好。
dp[i+1][ch[j][x]]+=dp[i][j],1<=x<=26。
注意即使不存在该结点(或者说连到了根节点上)也要累加,最后也要加上根节点上的dp值。

#include<cstdio>#include<cstring>const int md=10007;int ch[6010][30],fai[6010],dp[6010][6010],m,k,que[6010];bool b[6010];char s[110];int main(){    int i,j,n,p,q,x,y,z,hd,tl,tot;    scanf("%d%d",&n,&k);    for (i=1;i<=n;i++)    {        scanf("%s",s+1);        p=0;        for (j=1;j<=strlen(s+1);j++)        {            x=s[j]-'A'+1;             if (ch[p][x]) p=ch[p][x];            else p=ch[p][x]=++m;        }        b[p]=1;    }    hd=1;    tl=0;    for (i=1;i<=26;i++)      if (ch[0][i]) que[++tl]=ch[0][i];    while (hd<=tl)    {        p=que[hd++];        for (i=1;i<=26;i++)          if (ch[p][i])          {            x=ch[p][i];            fai[x]=ch[fai[p]][i];            que[++tl]=x;            if (b[fai[x]]) b[x]=1;          }          else            ch[p][i]=ch[fai[p]][i];    }    for (i=1;i<=26;i++)      if (!b[ch[0][i]]) dp[1][ch[0][i]]++;    for (i=0;i<k;i++)      for (j=0;j<=m;j++)        if (dp[i][j])          for (x=1;x<=26;x++)            if (!b[ch[j][x]])              dp[i+1][ch[j][x]]=(dp[i+1][ch[j][x]]+dp[i][j])%md;    tot=0;    for (i=0;i<=m;i++)      tot=(tot+dp[k][i])%md;    x=1;    for (i=1;i<=k;i++)      x=(x*26)%md;    printf("%d\n",(x-tot+md)%md);}
0 0
原创粉丝点击