[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


考虑补集转化,我们求出不包含任何一个单词的文本个数,对所有的单词建立AC自动机,在建fail指针的时候延续终止标识符,直接dp即可

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
    int x=0,f=1;char ch=gc();
    for (;ch<'0'||ch>'9';ch=gc())   if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x<0)    putchar('-'),x=-x;
    if (x>9)    print(x/10);
    putchar(x%10+'0');
}
const int N=1e2,M=60,Mod=1e4+7;
struct S1{
    int trie[N*M+10][26],fail[N*M+10],root,tot;
    bool End[N*M+10];
    void insert(char *s){
        int len=strlen(s),p=root;
        for (int i=0;i<len;i++){
            if (!trie[p][s[i]-'A']) trie[p][s[i]-'A']=++tot;
            p=trie[p][s[i]-'A'];
        }
        End[p]=1;
    }
    void make_fail(){
        static int h[N*M+10];
        int head=1,tail=0;
        for (int i=0;i<26;i++)  if (trie[root][i])  h[++tail]=trie[root][i];
        for (;head<=tail;head++){
            int Now=h[head];
            End[Now]|=End[fail[Now]];
            for (int i=0;i<26;i++){
                if (trie[Now][i]){
                    int son=trie[Now][i];
                    fail[son]=trie[fail[Now]][i];
                    h[++tail]=son;
                }else   trie[Now][i]=trie[fail[Now]][i];
            }
        }
    }
}AC;//Aho-Corasick automaton
int mlt(int a,int b){
    int res=1;
    for (;b;b>>=1,a=1ll*a*a%Mod)    if (b&1)    res=1ll*res*a%Mod;
    return res;
}
int f[N+10][N*M+10];
int main(){
    int n=read(),m=read();
    for (int i=1;i<=n;i++){
        static char s[N+10];
        scanf("%s",s);
        AC.insert(s);
    }AC.make_fail();
    f[0][0]=1;
    for (int i=0;i<m;i++){
        for (int j=0;j<AC.tot;j++){
            if (!f[i][j])   continue;
            for (int k=0;k<26;k++){
                int son=AC.trie[j][k];
                if (AC.End[son])    continue;
                f[i+1][son]=(f[i+1][son]+f[i][j])%Mod;
            }
        }
    }
    int Ans=mlt(26,m);
    for (int i=0;i<=AC.tot;i++) Ans=(Ans-f[m][i])%Mod;
    printf("%d\n",(Ans+Mod)%Mod);
    return 0;
}

转载于:https://www.cnblogs.com/Wolfycz/p/10485816.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值