bzoj1030: [JSOI2007]文本生成器

本文详细解析了JSOI2007文本生成器问题,通过动态规划与AC自动机结合的方式,解决了给定多个模板串求至少包含一个模板串的固定长度文本数量的问题。

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

题目大意

给定n个模板串,求出至少包含1个模板串的长度为m的串的个数。

题解

乍一看和DNA Sequence非常相似。但发现这题里模板串较多较长,所以无法用矩阵快速次幂的方法解决。但是这题的要求构造的文本串长度较小,考虑动态规划。
考虑求出所有不包含任何模板串的文本串的个数,再用总数减去。
用AC自动机构造出所有的边。
f[i][j] 为走了i步到j点的方案总数。那么 f[i][j] 显然可以转移到 f[i+1][ch[j][k]] ,那么初始化 f[0][0]=1 ,按照顺序dp即可。
答案即为 26mΣf[m][i]

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int max_size = 6000 + 10, ch_size = 26, mod = 10007, M = 100 + 10;
char s[M];
int n, m, d[M][max_size], ans;

struct Trie{
    int ch[max_size][ch_size];
    int val[max_size], f[max_size], q[max_size];
    int idx['Z'+1], sz;
    Trie() {sz = 1; for(char i = 'A'; i <= 'Z'; i++) idx[i] = i-'A';}

    void insert(char *s){
        int u = 0, len = strlen(s);
        for(int i = 0; i < len; i++){
            int x = idx[s[i]];
            if(!ch[u][x])
                ch[u][x] = sz++;
            u = ch[u][x];
        }
        val[u] = 1;
    }

    void get_fail(){
        int l = 1, r = 1;
        for(int i = 0; i < ch_size; i++){
            int u = ch[0][i];
            if(u){
                q[r++] = u;
            }
        }
        while(l < r){
            int t = q[l++];
            if(val[f[t]]) val[t] = 1;
            for(int i = 0; i < ch_size; i++){
                int &u = ch[t][i];
                if(!u){
                    u = ch[f[t]][i];
                    continue;
                }
                q[r++] = u;
                int v = f[t];
                while(v && !ch[v][i]) v = f[v];
                f[u] = ch[f[t]][i];
            }
        }
    }

    void solve(){
        d[0][0] = 1;
        for(int i = 1; i <= m; i++){
            for(int j = 0; j < sz; j++){
                if(val[j]) continue;
                for(int k = 0; k < ch_size; k++)
                    d[i][ch[j][k]]  = (d[i][ch[j][k]] + d[i-1][j]) % mod;
            }
        }
        int tot1 = 1, tot2 = 0;;
        for(int i = 1; i <= m; i++) tot1 = (tot1 * 26) % mod;
        for(int i = 0; i < sz; i++) if(!val[i]) tot2 = (tot2 + d[m][i]) % mod;
        ans = (tot1 - tot2 + mod) % mod;
    }
}ac;

void init(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++){
        scanf("%s", s);
        ac.insert(s);
    }
}

void work(){
    ac.get_fail();
    ac.solve();
    printf("%d\n", ans);
}

int main(){
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值