BZOJ1030: [JSOI2007]文本生成器

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
 
 
考虑用总方案数-不合法方案数

用f[i][j]表示长度为i位于Tire树中j号节点时所有不合法的方案数。
若当前处于j号节点,且Next[j][k]不是一个字符串的结尾,那么就可以由j号节点向Next[j][k]转移,即f[i][Next[j][k]]=f[i][Next[j][k]]+f[i-1][j]
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int p=10007;
const int N=150;
const int M=1e4+50;
int n,m;
char s[N];
struct acmach
{
    int Next[M][26],Fail[M],End[M],f[N][M];
    int root,L;
    int newnode()
    {
        for (int i=0;i<26;i++) Next[L][i]=-1;
        End[L]=0;
        return L++;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void Insert(char s[])
    {

        int len=strlen(s);
        int now=root;
        for (int i=0;i<len;i++)
        {
            if (Next[now][s[i]-'A']==-1)
                Next[now][s[i]-'A']=newnode();
            now=Next[now][s[i]-'A'];
        }
        End[now]++;
    }
    void build()
    {
        queue<int>q;
        Fail[root]=root;    //当前节点now的失败指针指向的地方
        for (int i=0;i<26;i++)
        if (Next[root][i]==-1) Next[root][i]=root; //下一个字母为i+'a'的节点的下标为Next[now][i]
        else
        {
            Fail[Next[root][i]]=root;
            q.push(Next[root][i]);
        }
        while (!q.empty())
        {
            int now=q.front(); q.pop();
            for (int i=0;i<26;i++)
            if (Next[now][i]==-1) Next[now][i]=Next[Fail[now]][i]; //指向当前节点fail指针的这个子节点
            else
            {
                Fail[Next[now][i]]=Next[Fail[now]][i]; //这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
                q.push(Next[now][i]);
            }
            End[now]+=End[Fail[now]];
        }
    }
    int query()
    {
        f[0][0]=1; //f[i][j]为当前长度为i的字符串处于Trie树中的第j号结点所具有的方案数。
        for (int i=1;i<=m;i++)
        for (int j=0;j<L;j++)
        {
            if (End[j]) continue;
            for (int k=0;k<26;k++)
            {
                if(End[Next[j][k]]) continue;
                f[i][Next[j][k]]=(f[i][Next[j][k]]+f[i-1][j])%p;
            }
        }
        int ret=1,sum=0;
        for (int i=1;i<=m;i++) ret=ret*26%p;
        for (int i=0;i<L;i++) sum=(sum+f[m][i])%p;
        return (ret-sum+p)%p;
    }
}ac;
int main()
{
    ac.init();
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s);
        ac.Insert(s);
    }
    ac.build();
    printf("%d\n",ac.query());
    return 0;
}
/*
2 2
A
B
*/
View Code

 

posted @ 2019-07-29 10:23  特特w  阅读(178)  评论(0编辑  收藏  举报