题解
经典的卷积求字符串匹配问题,但是我看错题+太菜,没想到用FTT做。
先忽略通配符的影响,分字符计算出每个起点的最大匹配字符个数,卷积的时候让T串逆序即可。
考虑通配符的贡献,贡献为S中通配符的个数 + T中通配符的个数 - S和T中对应位置都是通配符的个数。前两项可以用前缀和计算,后一项可以用FTT计算。
注意FTT使用非递归写法,递归写法容易T。
#include <bits/stdc++.h>
#include <cmath>
#include <complex>
#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {
return b ? gcd(b, a % b) : a;
}
#define INF 0x3f3f3f3f
double PI = acos(-1);
typedef std::complex<double> Comp; // STL complex
const Comp I(0, 1); // i
const int N = 2e6;
Comp tmp[N];
Comp f1[N], f2[N];
int rev[N];
void change(Comp y[], int len) {
for (int i = 0; i < len; ++i) {
rev[i] = rev[i >> 1] >> 1;
if (i & 1) { // 如果最后一位是 1,则翻转成 len/2
rev[i] |= len >> 1;
}
}
for (int i = 0; i < len; ++i) {
if (i < rev[i]) { // 保证每对数只翻转一次
swap(y[i], y[rev[i]]);
}
}
return;
}
void fft(Comp y[], int len, int on) {
change(y, len);
for (int h = 2; h <= len; h <<= 1) { // 模拟合并过程
Comp wn(cos(2 * PI / h), sin(on * 2 * PI / h)); // 计算当前单位复根
for (int j = 0; j < len; j += h) {
Comp w(1, 0); // 计算当前单位复根
for (int k = j; k < j + h / 2; k++) {
Comp u = y[k];
Comp t = w * y[k + h / 2];
y[k] = u + t; // 这就是把两部分分治的结果加起来
y[k + h / 2] = u - t;
// 后半个 “step” 中的ω一定和 “前半个” 中的成相反数
// “红圈”上的点转一整圈“转回来”,转半圈正好转成相反数
// 一个数相反数的平方与这个数自身的平方相等
w = w * wn;
}
}
}
if (on == -1) {
for (int i = 0; i < len; i++) {
y[i] /= len;
}
}
}
int get(int x) {
int res = 1;
while(res < x)
res = (res << 1);
return res;
}
char s1[N], s2[N];
int spre[N], ssum, scount[N];
void init(int len) {
for(int i = 0; i <= len; i++) {
f1[i] = f2[i] = Comp(0, 0);
}
}
int ans[N];
int res[N];
int main() {
IOS;
int k;
cin >> k;
while(k--) {
int n, m;
cin >> n >> m;
for(int i = 0; i < n; i++) ans[i] = res[i] = 0;
int len = get(max(n, m));
init(len);
cin >> s1 >> s2;
reverse(s2, s2 + m);
ssum = 0;
for(int i = 0; i < n; i++) {
spre[i] = 0;
if(s1[i] == '*') {
spre[i] = 1;
f1[i] = Comp(1, 0);
}
spre[i] += spre[i - 1];
}
for(int i = 0; i < m; i++) {
if(s2[i] == '*') {
ssum++;
f2[i] = Comp(1, 0);
}
}
fft(f1, len, 1);
fft(f2, len, 1);
for(int i = 0; i < len; i++) {
f1[i] *= f2[i];
}
fft(f1, len, -1);
for(int i = 0; i < len; i++) {
scount[i] = (int)round(f1[i].real());
}
for(int i = 0; i < 10; i++) {
init(len);
for(int j = 0; j < n; j++) {
if(s1[j] == i + '0') {
f1[j] = Comp(1, 0);
}
}
for(int j = 0; j < m; j++) {
if(s2[j] == i + '0') {
f2[j] = Comp(1, 0);
}
}
fft(f1, len, 1);
fft(f2, len, 1);
for(int j = 0; j < len; j++) {
f1[j] *= f2[j];
}
fft(f1, len, -1);
for(int j = m - 1; j < n; j++) {
ans[j] += (int)round(f1[j].real());
}
}
for(int i = m - 1; i < n; i++) {
ans[i] += (spre[i] + ssum - scount[i]);
if(i - m >= 0) ans[i] -= spre[i - m];
assert(m - ans[i] >= 0);
res[m - ans[i]]++;
}
for(int i = 1; i <= m; i++) {
res[i] += res[i - 1];
}
for(int i = 0; i <= m; i++) {
cout << res[i] << endl;
}
}
}