ac自动机

#


# 洛谷P4052_文本生成器

# 🔗

20221010215054

# 💡

正难则反,这个是让我们求含有可读文本的串数,我们可以尝试求一下不含有可读文本的串数
可读文本会在自动机插入时的所有尾节点被标记,同时含有可读文本的也可能是以被标记节点为后缀的串
故首先对自动机 trietrie 部分插入时的尾部标记为 11 ,一个串如果后缀是可读文本那么自己也不行,故要 oror 上自己的 failfail 指向的标记
既然是计数,就在这个自动机上 dpdp
设置 dp[i][j]dp[i][j] 表示文本长度为 ii ,到节点 jj 的目标数量
在自动机上往下走并转移,即枚举长度,枚举节点 uu ,枚举 2626 个子节点 vv ,如果 v,uv,u 被标记那么都不转移,否则令 dp[i][v]+=dp[i1][u]dp[i][v]+=dp[i-1][u]
最后统计 dp[m][i]dp[m][i]ii 没有被标记的数量和
26m26^m 减去它即可

#

const int N = 6010;
const int mod = 1e4 + 7;

struct AcAutoMaton {
    int ct, root;
    int fa[N], to[N][26];
    bool mark[N];
    inline AcAutoMaton () {
        ct = root = 1;
    }
    inline void insert (string s) {
        int p = root;
        for (char c : s) {
            if (!to[p][c - 'A']) to[p][c - 'A'] = ++ct;
            p = to[p][c - 'A'];
        }
        mark[p] = true;
    }
    inline void build (vector<string> v) {
        for (string it : v) insert(it);
        for (int i = 0; i < 26; i ++) to[0][i] = root;
        queue<int> que; que.push(root);
        while (!que.empty()) {
            int u = que.front(); que.pop();
            for (int i = 0; i < 26; i ++) {
                if (to[u][i]) {
                    fa[to[u][i]] = to[fa[u]][i];
                    mark[to[u][i]] |= mark[fa[to[u][i]]];
                    que.push(to[u][i]);
                } else {
                    to[u][i] = to[fa[u]][i];
                }
            }
        }
    }
} acam;

int dp[110][N];

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m; cin >> n >> m;
    vector<string> v(n); for (string &it : v) cin >> it;

    acam.build(v);

    dp[0][1] = 1;
    for (int i = 1; i <= m; i ++) {
        for (int j = 1; j <= acam.ct; j ++) {
            for (int c = 0; c < 26; c ++) {
                if (!acam.mark[j] && !acam.mark[acam.to[j][c]]) {
                    (dp[i][acam.to[j][c]] += dp[i - 1][j]) %= mod;
                }
            }
        }
    }

    int res = 1;
    for (int i = 0; i < m; i ++) res = res * 26 % mod;
    for (int i = 1; i <= acam.ct; i ++) {
        (res -= dp[m][i]) %= mod;
    }
    cout << (res % mod + mod) % mod << endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

Last Updated: 10/14/2023, 7:51:49 PM