bzoj1030 文本生成器

175 篇文章 0 订阅
78 篇文章 0 订阅

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值