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

orz AC自动机。

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=1030

Solution

AC 自动机上 DP 经典题。
首先断定,「至少一个」是不好做的。
于是转化一下,用 26 m 减去「一个串都没有」。
把所有的单词串建成 AC 自动机。
然后就能设计出一个最经典的 AC 自动机上 DP 模型了!
f [ i ] [ u ] 表示前 i 个字符,走到 AC 自动机上的节点 u 的串数。
为了方便一些萌新,这里讲一下「走到节点 u 」的具体含义:
将前 i 个字符组成的串在 AC 自动机上运行(按顺序对于每个字符,从根节点开始,对于每个字符,往对应的字符边走,这样一共走 i 步),最后到达的节点为 u
当然,为了确保「一个串都没有」,这里的 u 必须满足:
节点 u 表示的字符串的任意一个前缀都不等于任意一个单词串。
这等价于 u 在 Trie 上的祖先(包括自己)中,任意一个点都没有被标记为单词串。可以用 AC 自动机的 fail 来判断。
转移也就是枚举第 i + 1 个字符 c

f [ i + 1 ] [ g o [ u ] [ c ] ] + = f [ i ] [ u ]

这样,不包含任意单词串的文章总数为:
u f [ m ] [ u ]

26 m 减去上式,就得到最终需要输出的答案。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
const int N = 105, E = 1e4 + 5, PYZ = 10007;
int n, m, QAQ, que[E], len, f[N][E]; char s[N]; bool orz[E];
struct cyx {
    int go[26], cnt, fail; void init() {memset(go, 0, sizeof(go)); cnt = fail = 0;}
} T[E];
void ins() {
    int i, len = strlen(s + 1), u = 1; For (i, 1, len) {
        int c = s[i] - 'A'; if (!T[u].go[c]) T[T[u].go[c] = ++QAQ].init();
        u = T[u].go[c];
    }
    T[u].cnt++;
}
void cyx_dalao() {
    int i, c; T[0].init(); For (i, 0, 25) T[0].go[i] = 1;
    T[que[len = 1] = 1].fail = 0; For (i, 1, len) {
        int u = que[i]; For (c, 0, 25)
            if (!T[u].go[c]) T[u].go[c] = T[T[u].fail].go[c];
            else {
                int v = T[u].go[c], w = T[u].fail;
                while (!T[w].go[c]) w = T[w].fail;
                T[v].fail = T[w].go[c]; que[++len] = v;
                orz[v] = orz[T[v].fail] || T[v].cnt;
            }
    }
}
int DP() {
    int i, j, c, ans = 0, sum = 1; For (i, 1, m) sum = sum * 26 % PYZ;
    f[0][1] = 1; For (i, 0, m - 1) For (j, 1, QAQ) if (!orz[j])
        For (c, 0, 25) if (!orz[T[j].go[c]]) {
            int x = T[j].go[c]; f[i + 1][x] = (f[i + 1][x] + f[i][j]) % PYZ;
        }
    For (j, 1, QAQ) ans = (ans + f[m][j]) % PYZ; return (sum - ans + PYZ) % PYZ;
}
int main() {
    int i; cin >> n >> m; T[QAQ = 1].init();
    For (i, 1, n) scanf("%s", s + 1), ins(); cyx_dalao(); cout << DP() << endl;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/80919301
今日推荐