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

tech2023-09-27  97

题意

n n n 个单词,现在要生成 m m m 个字母的文本,问至少出现一个单词的文本有多少种。 n ≤ 60 , m ≤ 100 , ∣ s i ∣ ≤ 100 n\leq 60,m\leq 100,|s_i|\leq 100 n60,m100,si100 s i s_i si 只包含大写英文字母。

分析

这算是一道很典型的 A C AC AC 自动机上 d p dp dp 的题目。 答案就是用 2 6 m 26^m 26m 减去一个单词都没出现过的。现在我们求一下后面的东西。 令 f i , j f_{i,j} fi,j 表示长度为 i i i,来到节点 j j j 的方案数。 那么如果 t r j , k tr_{j,k} trj,k 不是末尾节点,就可以转移到 t r j , k tr_{j,k} trj,k。 初始条件 f 0 , 0 = 1 f_{0,0}=1 f0,0=1。 总复杂度是 O ( 26 n ∣ s ∣ m ) O(26n|s|m) O(26nsm)

代码如下

#include <bits/stdc++.h> #include<ext/pb_ds/hash_policy.hpp> #include<ext/pb_ds/assoc_container.hpp> using namespace __gnu_pbds; using namespace std; typedef long long LL; typedef unsigned long long uLL; struct custom_hash { static uint64_t splitmix64(uint64_t x) { x += 0x9e3779b97f4a7c15; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb; return x ^ (x >> 31); } size_t operator()(uint64_t x) const { static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count(); return splitmix64(x + FIXED_RANDOM); } }; LL z = 1; int read(){ int x, f = 1; char ch; while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1; x = ch - '0'; while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48; return x * f; } int ksm(int a, int b, int p){ int s = 1; while(b){ if(b & 1) s = z * s * a % p; a = z * a * a % p; b >>= 1; } return s; } const int mod = 1e4 + 7, N = 6e3 + 5; int f[105][N], ch[N][26], fail[N], v[N], cnt; char s[N]; void insert(char *s){ int i, p = 0, c, n = strlen(s); for(i = 0; i < n; i++){ c = s[i] - 'A'; if(!ch[p][c]) ch[p][c] = ++cnt; p = ch[p][c]; } v[p] = 1; } void get_fail(){ int i, a; queue<int> q; for(i = 0; i < 26; i++) if(ch[0][i]) q.push(ch[0][i]); while(!q.empty()){ a = q.front(); q.pop(); for(i = 0; i < 26; i++){ if(ch[a][i]) v[ch[a][i]] |= v[ch[fail[a]][i]], fail[ch[a][i]] = ch[fail[a]][i], q.push(ch[a][i]);//一定要更新 v[ch[a][i]]! else ch[a][i] = ch[fail[a]][i]; } } } int main(){ int i, j, k, n, m, ans; n = read(); m = read(); for(i = 1; i <= n; i++){ scanf("%s", s); insert(s); } get_fail(); ans = ksm(26, m, mod); f[0][0] = 1; for(i = 0; i <= m; i++){ for(j = 0; j <= cnt; j++){ for(k = 0; k < 26; k++){ if(!v[ch[j][k]]){ f[i + 1][ch[j][k]] = (f[i + 1][ch[j][k]] + f[i][j]) % mod; } } } } for(i = 0; i <= cnt; i++) ans = (ans - f[m][i]) % mod; printf("%d", (ans + mod) % mod); return 0; }
最新回复(0)