2021杭电多校3 C Forgiving Matching

题目描述

https://acm.hdu.edu.cn/showproblem.php?pid=6975

Solution

我们依然考虑使用卷积来解决这个问题

在求完美匹配的时候,我们使用的是函数是 $(S_i-T_i)^2\times S_i \times T_i$

但是显然这个函数不能求有多少个位置不匹配

一个比较没有用的想法是改造这个函数,但这显然也不做到

那么我们换个思路,注意到 $S$ 和 $T$ 都只含有 $0$ 到 $9$ 以及通配符,我们考虑枚举数字 $k$

然后只算数字 $k$ 的贡献,换句话说只求 $T$ 中数字 $k$ 的失配个数

我们将 $T$ 中的 $k$ 映射成 $1$,其余字符都是通配符,将 $S$ 中的 $k$ 映射成 $1$,其他数字映射成 $2$,通配符还是 $0$

这样我们发现一个失配位置的权值一定是 $2$​​,那么这样就可以统计失配位置的个数了

另外为了加速可以将平方改成一次方,因为 $S_i$ 一定大于 $T_i$

时间复杂度 $O(10n\log n)$,需要注意常数

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define maxn 1200010
#define ll long long
using namespace std;

const int p = 1004535809;
const int G = 3;
const int Gi = 334845270;

ll pow_mod(ll x, ll n) {
ll s = 1;
for (; n; n >>= 1) {
if (n & 1) s = s * x % p;
x = x * x % p;
} return s;
}

int n, m;

char S[maxn], T[maxn];

int s[maxn], t[maxn];

int a[maxn], b[maxn], A[maxn], B[maxn];
inline int add(int x, int y) { return (x += y) >= p ? x - p : x; }

int P[maxn];
void init_P(int n) {
int l = 0; while ((1 << l) < n) ++l;
for (int i = 0; i < n; ++i) P[i] = (P[i >> 1] >> 1) | ((i & 1) << l - 1);
}

void NTT(int *a, int n, int type) {
static int w[maxn];
for (int i = 0; i < n; ++i) if (i < P[i]) swap(a[i], a[P[i]]);
for (int i = 2, m = 1; i <= n; m = i, i *= 2) {
ll wn = pow_mod(type > 0 ? G : Gi, (p - 1) / i);
w[0] = 1; for (int j = 1; j < m; ++j) w[j] = wn * w[j - 1] % p;
for (int j = 0; j < n; j += i)
for (int k = 0; k < m; ++k) {
ll t1 = a[j + k], t2 = 1ll * a[j + k + m] * w[k] % p;
a[j + k] = add(t1, t2);
a[j + k + m] = add(t1, p - t2);
}
}
if (type < 0) {
ll inv = pow_mod(n, p - 2);
for (int i = 0; i < n; ++i) a[i] = inv * a[i] % p;
}
}

void Mul(int *a, int *B, int n1, int n2) {
int n = 1; while (n < n1 + n2 - 1) n <<= 1; init_P(n);
for (int i = 0; i < n2; ++i) b[i] = B[i];
fill(a + n1, a + n, 0); fill(b + n2, b + n, 0);
NTT(a, n, 1); NTT(b, n, 1);
for (int i = 0; i < n; ++i) a[i] = 1ll * a[i] * b[i] % p;
NTT(a, n, -1);
}

ll f[maxn], g[maxn];
void solve() {
for (int i = 0; i < n; ++i) A[i] = s[i] * s[i];
for (int i = 0; i < m; ++i) B[i] = t[i] * t[i];
Mul(A, B, n, m);
for (int i = 0; i < n; ++i) f[i] = A[i];

for (int i = 0; i < n; ++i) A[i] = s[i];
for (int i = 0; i < m; ++i) B[i] = t[i] * t[i];
Mul(A, B, n, m);
for (int i = 0; i < n; ++i) f[i] = add(f[i], p - A[i]) % p;

for (int i = m - 1; i < n; ++i) g[i + 1] += f[i] / 2;
}

ll ans[maxn];
void work() {
cin >> n >> m >> S >> T; reverse(T, T + m);
for (int i = 1; i <= n; ++i) g[i] = 0;
for (int i = 0; i <= m; ++i) ans[i] = 0;
for (char o = '0'; o <= '9'; ++o) {
for (int i = 0; i < m; ++i)
if (T[i] == o) t[i] = 1;
else t[i] = 0;
for (int i = 0; i < n; ++i)
if (S[i] == o) s[i] = 1;
else if (S[i] == '*') s[i] = 0;
else s[i] = 2;
solve();
}
for (int i = m; i <= n; ++i) ++ans[g[i]];
for (int i = 0; i <= m; ++i) {
cout << ans[i] << "\n";
ans[i + 1] += ans[i];
}
}

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

int T; cin >> T;
while (T--) work();
return 0;
}