BZOJ 1030: [JSOI2007]文本生成器 [AC自动机 DP]

1030: [JSOI2007]文本生成器

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3953  Solved: 1614
[Submit][Status][Discuss]

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
 

题意:求长度为m且含有至少一个模板串的字符串个数

好神啊
含有至少一个不好求,那就求不含模板串的然后总数减
计数问题组合数学做不了就考虑DP
f[i][j]表示长度为i匹配到自动机上节点j的不含模板串的方案数
转移 f[i][j]-->f[i+1][t[j].ch[k]]
 
如何判断一个串不含模板串?首先f[i][j]已经保证1..i-1是不含模板串的啦,只要保证第i个字符加入后也不含就行了
ins时模板串终点打标记,本身且Fail祖先没有标记就说明AC自动机上j结尾的没有模板串
 
但是有的字符模板串里没有啊?给root把孩子补全就行了【实际上不补全也是可以的,反正默认走到了根,最后f[m][根]也统计】
 
思路和KMP一样,都是一边生成字符串一边在KMP/AC自动机上匹配,
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=105,M=6005,MOD=10007;
int n,m;
char s[N];
struct node{
    int ch[26],fail,val;
}t[M];
int sz;
void ins(char s[]){
    int u=0,n=strlen(s+1);
    for(int i=1;i<=n;i++){
        int c=s[i]-'A';
        if(!t[u].ch[c]) t[u].ch[c]=++sz;
        u=t[u].ch[c];
    }
    t[u].val=1;
}
int q[M],head,tail;
void getAC(){
    head=tail=1;
    for(int i=0;i<26;i++){
        if(!t[0].ch[i]) t[0].ch[i]=++sz;
        q[tail++]=t[0].ch[i];
    }
    while(head!=tail){
        int u=q[head++];
        t[u].val|=t[t[u].fail].val;
        for(int i=0;i<26;i++){
            int &v=t[u].ch[i];
            if(!v) {v=t[t[u].fail].ch[i];continue;}
            t[v].fail=t[t[u].fail].ch[i];
            q[tail++]=v;
        }
    }
}
int f[N][M],ans;
void dp(){
    f[0][0]=1;
    for(int i=1;i<=m;i++){
        for(int j=0;j<=sz;j++) if(!t[j].val&&f[i-1][j]!=0)
            for(int k=0;k<26;k++) if(!t[t[j].ch[k]].val)
                f[i][t[j].ch[k]]=(f[i][t[j].ch[k]]+f[i-1][j])%MOD;
    }
    for(int j=0;j<=sz;j++) ans=(ans+f[m][j])%MOD;
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);//printf("hi %d %d nm\n",n,m);
    for(int i=1;i<=n;i++) scanf("%s",s+1),ins(s);
    getAC();
    dp();
    int tmp=1;
    for(int i=1;i<=m;i++) tmp=(tmp*26)%MOD;
    printf("%d",(tmp-ans+MOD)%MOD);
}

 

 
 
 
 
 
posted @ 2016-12-25 18:26  Candy?  阅读(410)  评论(0编辑  收藏  举报