题源链接:https://www.luogu.org/problem/P4052
神仙题 搞不动搞不动
抓紧先写篇题解,过一会儿又该不会了
题意是求最多的可读文本数量 很明显是要在AC自动机上乱搞
一个很明显的思路就是在自动机上dp
dpi表示第i步走到第j个结点的方法数量 (貌似是AC自动机上dp小套路?
那么在每一个待匹配文本串末尾打上标记,跑一遍dp再求和是不是就OK了呢?
仔细一想这里面有太多容斥,写不动写不动
那我们换个思路:可读文本数量 = 所有的状况 - 不可读文本数量
所有状况显而易见是26^m
而不可读文本数量显然比较好搞,在自动机上状态转移,遇到打着标记的节点跳过即可
int cnt = 0;
for (int i = 1; i <= m; i++) {
for (int j = 0; j <= tot; j++) {
if (idx[j]) continue;
for (int k = 0; k < 26; k++) {
f[i][tr[j][k]] = (f[i][tr[j][k]] + f[i - 1][j]) % mod;
}
}
}
for (int i = 0; i <= tot; i++) {
if (!idx[i]) cnt = (cnt + f[m][i]) % mod;
}
值得注意的一点:
如果一个结点的fail指针指向了文本串的末尾,那这个结点代表的字符串必然也是可读的,也要打上标记。
#include <bits/stdc++.h>
#define numm ch - 48
#define pd putchar(' ')
#define pn putchar('\n')
#define pb push_back
#define fi first
#define se second
#define fre1 freopen("1.txt", "r", stdin)
#define fre2 freopen("2.txt", "w", stdout)
typedef long long int ll;
typedef long long int LL;
using namespace std;
template<typename T>
void read(T &res) {
bool flag = false;
char ch;
while (!isdigit(ch = getchar())) (ch == '-') && (flag = true);
for (res = numm; isdigit(ch = getchar()); res = (res << 1) + (res << 3) + numm);
flag && (res = -res);
}
template<typename T>
void write(T x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
//static auto _ = []() {
// ios::sync_with_stdio(false);
// cin.tie(0);
// return 0;
//}();
//
const int mod = 10007;
int n, m;
const int N = 156, L = 1e6 + 6;
namespace AC {
const int SZ = N * 80;
int tot, tr[SZ][26];
int fail[SZ], idx[SZ], val[SZ];
int f[150][SZ];
void init() {
memset(fail, 0, sizeof(fail));
memset(tr, 0, sizeof(tr));
memset(val, 0, sizeof(val));
memset(idx, 0, sizeof(idx));
tot = 0;
}
void insert(char *s, int id) { // id 表示原始字符串的编号
int u = 0;
for (int i = 1; s[i]; i++) {
if (!tr[u][s[i] - 'A']) tr[u][s[i] - 'A'] = ++tot;
u = tr[u][s[i] - 'A'];
}
idx[u] = 1;
}
queue<int> q;
void build() {
for (int i = 0; i < 26; i++)
if (tr[0][i]) q.push(tr[0][i]);
while (q.size()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
if (tr[u][i]) {
fail[tr[u][i]] = tr[fail[u]][i];
q.push(tr[u][i]);
} else {
tr[u][i] = tr[fail[u]][i];
}
if (idx[tr[fail[u]][i]]) idx[tr[u][i]] = 1;
}
}
}
int dp() {
int cnt = 0, ans = 1;
f[0][0] = 1;
for (int i = 1; i <= m; i++) {
for (int j = 0; j <= tot; j++) {
if(idx[j]) continue;
for (int k = 0; k < 26; k++) {
f[i][tr[j][k]] = (f[i][tr[j][k]] + f[i - 1][j]) % mod;
}
}
}
for (int i = 0; i <= tot; i++) {
if (!idx[i]) cnt = (cnt + f[m][i]) % mod;
}
for (int i = 1; i <= m; i++) {
ans = (ans * 26) % mod;
}
return (ans % mod - cnt % mod + mod) % mod;
}
} // namespace AC
char s[N][100];
int main() {
read(n), read(m);
AC::init();
for (int i = 1; i <= n; i++) scanf("%s", s[i] + 1), AC::insert(s[i], i);
AC::build();
write(AC::dp());
return 0;
}