洛谷传送门
BZOJ传送门
题目描述
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串 A A A和 B B B,其中 A A A串长度为 m m m, B B B串长度为 n n n。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。
你想对这两个串重新进行匹配,其中 A A A为模板串,那么现在问题来了,请回答,对于 B B B的每一个位置 i i i,从这个位置开始连续 m m m个字符形成的子串是否可能与 A A A串完全匹配?
输入输出格式
输入格式:
第一行包含两个正整数 m , n m,n m,n,分别表示 A A A串和 B B B串的长度。
第二行为一个长度为 m m m的字符串 A A A。
第三行为一个长度为 n n n的字符串 B B B。
两个串均仅由小写字母和 ∗ * ∗号组成,其中 ∗ * ∗号表示相应位置已经残缺。
输出格式:
第一行包含一个整数 k k k,表示 B B B串中可以完全匹配 A A A串的位置个数。
若 k > 0 k > 0 k>0,则第二行输出 k k k个正整数,从小到大依次输出每个可以匹配的开头位置(下标从 1 1 1开始)。
输入输出样例
输入样例#1:
3 7
a*b
aebr*ob
输出样例#1:
2
1 5
说明
100%的数据满足 1 ≤ m ≤ n ≤ 300000 1 \leq m \leq n \leq 300000 1≤m≤n≤300000。
解题分析
这种通配符匹配的问题 D P DP DP也能做, 但显然要跑一年才能跑出来… 因为字符串不全自动机也就 G G GG GG了…
所以我们考虑换一种思路, 如果没有通配符匹配的串会满足
∑
i
=
1
m
(
A
[
i
]
−
B
[
p
o
s
+
i
]
)
2
=
0
\sum_{i=1}^{m}(A[i]-B[pos+i])^2=0
i=1∑m(A[i]−B[pos+i])2=0
那么如果有通配符的话实际上其的权值设为0就行, 然后我们魔改一下这个式子就行了:
∑
i
=
1
m
A
[
i
]
×
(
A
[
i
]
−
B
[
p
o
s
+
i
]
)
2
×
B
[
i
]
=
0
\sum_{i=1}^{m}A[i]\times(A[i]-B[pos+i])^2\times B[i]=0
i=1∑mA[i]×(A[i]−B[pos+i])2×B[i]=0
但这玩意还是没法做, 我们把
A
A
A翻转一下上面那个式子就变成了一个卷积的形式, 做
7
7
7遍
F
F
T
FFT
FFT就好了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#define R register
#define IN inline
#define W while
#define db double
#define MX 2400500
const db PI = std::acos(-1.0);
struct Complex {db im, re;} a[MX], b[MX], c[MX];
IN Complex operator * (const Complex &x, const Complex &y)
{return {x.im * y.re + x.re * y.im, x.re * y.re - x.im * y.im};}
IN Complex operator + (const Complex &x, const Complex &y)
{return {x.im + y.im, x.re + y.re};}
IN Complex operator - (const Complex &x, const Complex &y)
{return {x.im - y.im, x.re - y.re};}
IN Complex operator * (const Complex &x, const db &y)
{return {x.im * y, x.re * y};}
char tar[MX], mod[MX];
int rev[MX], ans[MX];
db v1[MX], v2[MX];
int tot, sum, lg, cnt, len1, len2;
IN void FFT(Complex *dat, const int &typ)
{
for (R int i = 1; i < tot; ++i) if(rev[i] < i) std::swap(dat[rev[i]], dat[i]);
R int cur, seg, bd, step, now; Complex deal, base, buf1, buf2;
for (seg = 1; seg < tot; seg <<= 1)
{
step = seg << 1; base = {std::sin(PI / seg) * typ, std::cos(PI / seg)};
for (now = 0; now < tot; now += step)
{
deal = {0, 1}; bd = now + seg;
for (cur = now; cur < bd; ++cur, deal = deal * base)
{
buf1 = dat[cur], buf2 = dat[cur + seg] * deal;
dat[cur] = buf1 + buf2, dat[cur + seg] = buf1 - buf2;
}
}
}
}
int main(void)
{
scanf("%d%d", &len1, &len2);
scanf("%s%s", tar, mod);
for (R int i = 0; i < len1; ++i) v1[len1 - i - 1] = (tar[i] == '*') ? 0 : tar[i] - 'a' + 1;
for (R int i = 0; i < len2; ++i) v2[i] = (mod[i] == '*') ? 0 : mod[i] - 'a' + 1;
sum = len1 + len2;
for (tot = 1; tot <= sum; tot <<= 1, ++lg); tot <<= 1, ++lg;
for (R int i = 1; i < tot; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
for (R int i = 0; i < tot; ++i) a[i] = {0, v1[i] * v1[i] * v1[i]}, b[i] = {0, v2[i]};
FFT(a, 1), FFT(b, 1);
for (R int i = 0; i < tot; ++i) c[i] = a[i] * b[i];
for (R int i = 0; i < tot; ++i) a[i] = {0, v1[i] * v1[i]}, b[i] = {0, v2[i] * v2[i]};
FFT(a, 1), FFT(b, 1);
for (R int i = 0; i < tot; ++i) c[i] = c[i] - a[i] * b[i] * 2.0;
for (R int i = 0; i < tot; ++i) a[i] = {0, v1[i]}, b[i] = {0, v2[i] * v2[i] * v2[i]};
FFT(a, 1), FFT(b, 1);
for (R int i = 0; i < tot; ++i) c[i] = c[i] + a[i] * b[i];
FFT(c, -1); int bd = len2 - len1;
for (R int i = 0; i <= bd; ++i) if(fabs(c[i + len1 - 1].re) < 0.5) ans[++cnt] = i + 1;
printf("%d\n", cnt);
for (R int i = 1; i <= cnt; ++i) printf("%d ", ans[i]);
}