[JSOI2007] text generator DP+AC automaton

Description
Randomly generate an article. If an article contains at least one word that users know, then we say the article is readable (we call article a contains word b, if and only if word b is a substring of article a).
Now give the N words that your users know, randomly generate articles of length M, and find out how many articles are readable.

Sample Input
2 2
A
B

Sample Output
100

This is a good question...
First of all, consider DP, and set f[i][j] as the number of readable articles matched to the position numbered j on the AC machine from the i-th bit.
For state f[i-1][j], consider what state it can update.
For the current i,j, enumerate 1-26 times, skip fail for each character, and skip to the inheritable position gg.
Then the DP equation is: 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;
}

Posted by ticallian on Thu, 02 Apr 2020 23:37:51 -0700