[JSOI2007]文本生成器 DP+AC自动机

Description
随机生成一篇文章,如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。
现在给出你使用者了解的N个单词,随机生成长度为M的文章,求有多少个文章是可读的。


Sample Input
2 2
A
B


Sample Output
100


这题好题啊。。。
首先考虑DP,设f[i][j]为到第i位匹配到AC机上编号为j的位置的可读文章数。
对于状态f[i-1][j]考虑它可以更新什么状态。
对于当前i,j,枚举一遍1~26,对于每个字符都跳fail,跳到可继承的位置gg。
则可得DP方程为:f[i][gg]+=f[i-1][j]。


#include <cstdio>
#include <cstring>

using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
const int maxn = 110;
const int mod = 10007;

struct node {
    int fail, v[30];
    node() {memset(v, -1, sizeof(v));}
} t[maxn * 60]; int cnt, list[maxn * 60];
int cc[maxn * 60], f[maxn][60 * maxn];
char ss[maxn];

void bt() {
    int x = 0;
    int len = strlen(ss + 1);
    for(int i = 1; i <= len; i++) {
        int y = ss[i] - 'A' + 1;
        if(t[x].v[y] == -1) t[x].v[y] = ++cnt;
        x = t[x].v[y];
    }
    cc[x] = 1;
}

void get_fail() {
    int head = 1, tail = 2;
    list[1] = 0;
    while(head != tail) {
        int x = list[head];
        for(int i = 1; i <= 26; i++) {
            int y = t[x].v[i];
            if(y == -1) continue;
            if(x == 0) t[y].fail = 0;
            else {
                int j = t[x].fail;
                while(j && t[j].v[i] == -1) j = t[j].fail;
                t[y].fail = _max(0, t[j].v[i]);
            }
            list[tail++] = y;
        }
        if(cc[t[x].fail]) cc[x] = 1;
        head++;
    }
}

int main() {
    int n, m; scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%s", ss + 1);
        bt();
    }
    get_fail();
    f[0][0] = 1;
    int kk = 1;
    for(int i = 1; i <= m; i++) {
        (kk *= 26) %= mod;
        for(int j = 0; j <= cnt; j++) {
            if(cc[j] || !f[i - 1][j]) continue;
            for(int k = 1; k <= 26; k++) {
                int u = j;
                while(u && t[u].v[k] == -1) u = t[u].fail;
                if(t[u].v[k] == -1) (f[i][0] += f[i - 1][j]) %= mod;
                else (f[i][t[u].v[k]] += f[i - 1][j]) %= mod;
            }
        }
    }
    int ans = 0;
    for(int i = 0; i <= cnt; i++) if(!cc[i]) (ans += f[m][i]) %= mod;
    (ans = kk - ans) %= mod;
    printf("%d\n", (ans + mod) % mod);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/80069091
今日推荐