Dyd's Blog

He who has a strong enough why can bear almost any how.

CF1327G Letters and Question Marks

AC 自动机 + 状压 dp

Letters and Question Marks

思路

套路设 $d[i, j, k]$ 表示“匹配到 $S$ 的第 $i$ 位,在 ACAM 的第 $j$ 个节点,已经填的字符集合为 $k$ 的方案数”,看见 $\mid S \mid$ 很大但是 $?$ 较少,且当 $?$ 确定后两个 $?$ 间的转移是确定的,考虑到 ACAM 的大小也不大,不妨把第一维 $i$ 改称“匹配到第 $i$ 个 $?$ ”,预处理出第 $i$ 个 $?$ 处对应 ACAM 上任意节点为起点的转移即可

记 $T = \sum \mid t_i \mid$ ,时间复杂度为 $O(T \mid S \mid + 2^{14}T*14)$

代码

需要注意的是 $fail[x]$ 的编号不一定比 $x$ 小

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb emplace_back
#define pct(x) __builtin_popcount(x)
using LL = long long;
const LL INF = 1e18;
const int T = 1000 + 100, S = 4e5 + 100, A = 14;
int n, cnt;
std::pair<int, LL> trans[A + 5][T];
char s[S];
LL d[(1 << A) + 100][T], ans = -INF;
bool vis[(1 << A) + 100][T];
struct ACAM
{
int ch[T][A + 5], fail[T], tot;
LL ed[T];
void ins(char *s, int v)
{
int p = 0;
for (int c; *s; ++s)
{
c = *s - 'a';
if (!ch[p][c]) ch[p][c] = ++tot;
p = ch[p][c];
}
ed[p] += v;
}
void bd()
{
std::queue<int> q;
fail[0] = 0;
for (int i = 0; i < A; ++i) if (ch[0][i]) fail[ch[0][i]] = 0, q.push(ch[0][i]);
while (!q.empty())
{
int x = q.front(); q.pop();
for (int i = 0, t; i < A; ++i)
if (t = ch[x][i])
{
fail[t] = ch[fail[x]][i], q.push(t);
ed[t] += ed[fail[t]];
}
else ch[x][i] = ch[fail[x]][i];
}
}
std::pair<int, LL> walk(int p, int pos)
{
LL res = 0;
while (s[pos] && s[pos] != '?')
{
p = ch[p][s[pos++] - 'a'];
res += ed[p];
}
return {p, res};
}
} ac;
void upd(int used, int x, LL v){ d[used][x] = std::max(v, d[used][x]), vis[used][x] = true; }
int main()
{
{
int num, c;
char t[T];
scanf("%d", &num);
for (int i = 1; i <= num; ++i) scanf("%s %d", t + 1, &c), ac.ins(t + 1, c);
ac.bd();
}
scanf("%s", s + 1), n = strlen(s + 1);
for (int i = 1; i <= n; ++i) if (s[i] == '?')
{
++cnt;
for (int j = ac.tot; ~j; --j) trans[cnt][j] = ac.walk(j, i + 1);
}
for (int i = (1 << A) - 1; ~i; --i)
for (int j = ac.tot; ~j; --j) d[i][j] = -INF, vis[i][j] = false;
auto t = ac.walk(0, 1); upd(0, t.fi, t.se);
for (int i = 0, p; i < (1 << A); ++i) if (pct(i) < cnt)
for (int j = ac.tot; ~j; --j) if (vis[i][j])
{
LL td = d[i][j];
for (int k = 0; k < A; ++k) if (!((i >> k) & 1))
{
p = ac.ch[j][k];
t = trans[pct(i) + 1][p];
upd(i | (1 << k), t.fi, td + t.se + ac.ed[p]);
}
}
for (int i = (1 << A) - 1; ~i; --i) if (pct(i) == cnt)
for (int j = ac.tot; ~j; --j) ans = std::max(ans, d[i][j]);
printf("%lld\n", ans);
return 0;
}