[BZOJ1030][JSOI2007]文本生成器

[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

Solution :
直接算好像比较困难,所以考虑先算不可读的串的个数,再拿总串数去减。
不可读的串的数量就是在AC自动机上走M步而不经过结尾节点(包括结尾点和fail指向结尾点的节点)的路径条数。
这个怎么求呢?
f[i][j]ij
f[i][j]f[i+1][son[j][k]]
i+1k
f[m][i]

Code :

#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define REP(i, n) for (int i = 0; i < n; i++)
#define PER(i, n) for (int i = (n)-1; i >= 0; i--)
#define fst first
#define sec second
#define MS(_) memset(_, 0, sizeof(_))
#define PB push_back
#define MP make_pair

template<typename _T> inline void read(_T &x){
    x = 0; bool f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = 0; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x = f ? x : -x;
}
const int M = 111;
const int L = 6666;
const int MOD = 10007;
struct Node{
    int cnt, num; Node *nxt[26], *fail;
}pool[L], *tail = pool, *rt;
int dp[M][L], n, m, q[L], tot = 1;
char st[L];
inline void ins(Node *&rt, char *p){
    if (!rt) tail->num = tot++, rt = tail++;
    if (!*p) rt->cnt = 1; else ins(rt->nxt[*p-'A'], p+1);
}
inline void build(){ int l, r;
    rt->fail = rt;
    for(q[l = r = 0] = (int)rt; l <= r; l++){
        Node *p = (Node *)q[l];
        REP(i, 26)
            if (p->nxt[i]) q[++r] = (int)(p->nxt[i]), p->nxt[i]->fail = p == rt ? rt : p->fail->nxt[i];
            else p->nxt[i] = p == rt ? rt : p->fail->nxt[i];
        p->cnt |= p->fail->cnt;
    }
}
inline void solve(){
    dp[0][1] = 1;
    rep(i, 1, m)
        for(Node *j = pool; j != tail; j++){
            if (j->cnt) continue;
            REP(k, 26) if (j->nxt[k]) (dp[i][j->nxt[k]->num] += dp[i-1][j->num]) %= MOD;
    }
    int ans = 0, tot = 1;
    for(Node *i = pool; i != tail; i++) if(!i->cnt) (ans += dp[m][i->num]) %= MOD;
    rep(i, 1, m) (tot *= 26) %= MOD;
    printf("%d\n", ((tot-ans)%MOD+MOD)%MOD);
}


int main(){
    read(n); read(m);
    rep(i, 1, n) scanf("%s", st), ins(rt, st);
    build();
    solve();
    return 0;          
}

你可能感兴趣的:(dp,AC自动机)