给出文本串 S S 和模板串 , T T 中可能有通配符,求 中 T T 所有出现位置。
FFT神仙题。
在没有通配符的情况下,考虑两个长度为 n n 的串 相等的充要条件是
可以变形为
自然的,我们想到
但这并不能推出 S=T S = T ,所以我们将其修正为
现在考虑通配符,如果 Ti T i 是一个通配符,我们就让它的值为 0 0 ,同时其他字符的值均不为 ,那么可得
将式子展开,得
将 T T <script type="math/tex" id="MathJax-Element-26">T</script> 翻转后对三项分别FFT即可
#include <bits/stdc++.h>
#define IL __inline__ __attribute__((always_inline))
#define For(i, a, b) for (int i = a, i##end = b; i <= i##end; ++ i)
#define FOR(i, a, b) for (int i = a, i##end = b; i < i##end; ++ i)
#define Rep(i, a, b) for (int i = a, i##end = b; i >= i##end; -- i)
#define REP(i, a, b) for (int i = a, i##end = b; i > i##end; -- i)
typedef long long LL;
template < class T >
IL T chkmax(T &a, const T b) {
if (a < b)
a = b;
return a;
}
template < class T >
IL T chkmin(T &a, const T b) {
if (a > b)
a = b;
return a;
}
template < class T >
IL T mymax(const T a, const T b) {
return a > b ? a : b;
}
template < class T >
IL T mymin(const T a, const T b) {
return a < b ? a : b;
}
template < class T >
IL T myabs(const T &a) {
return a > 0 ? a : -a;
}
const int INF = 0X3F3F3F3F;
const double EPS = 1E-10, PI = acos(-1.0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define OK DEBUG("Passing [%s] in LINE %d...\n", __FUNCTION__, __LINE__)
#define F first
#define S second
/*************************header*************************/
const int MAXN = 100000 + 5;
char s[MAXN], t[MAXN];
int len1, len2;
namespace Polynomial {
const int MOD = 998244353, G = 3, Ginv = 332748118;
IL int add(int a, int b) {
a += b;
return a >= MOD ? a - MOD : a < 0 ? a + MOD : a;
}
IL int mul(int a, int b) {
return 1LL * a * b % MOD;
}
IL int FastPow(int a, int p) {
int res = 1;
for (int base = a; p; base = mul(base, base), p >>= 1)
if (p & 1)
res = mul(res, base);
return res;
}
int pos[MAXN * 4], rev;
IL void Init(int n) {
FOR(i, 0, n)
pos[i] = (pos[i >> 1] >> 1) | ((i & 1) << (rev - 1));
}
IL void NTT(int *a, int n, bool t) {
FOR(i, 0, n)
if (i > pos[i])
std::swap(a[i], a[pos[i]]);
for (int i = 2; i <= n; i <<= 1) {
int m = i >> 1, w = FastPow(t ? G : Ginv, (MOD - 1) / i);
for (int *p = a; p != a + n; p += i) {
int o = 1;
FOR(j, 0, m) {
int tmp = mul(o, p[j + m]);
p[j + m] = add(p[j], -tmp);
p[j] = add(p[j], tmp);
o = mul(o, w);
}
}
}
}
IL void DFT(int *a, int n) {
NTT(a, n, 1);
}
IL void IDFT(int *a, int n) {
NTT(a, n, 0);
int inv = FastPow(n, MOD - 2);
FOR(i, 0, n)
a[i] = mul(a[i], inv);
}
}
int x[MAXN * 4], y[MAXN * 4], ans[MAXN * 4], pos[MAXN];
int main() {
using namespace Polynomial;
scanf("%s%s", s, t);
len1 = strlen(s), len2 = strlen(t);
std::reverse(t, t + len2);
int sz = 1;
for (; sz <= len1 + len2; sz <<= 1, ++ rev);
Init(sz);
#define id(x) ((x) == '?' ? 0 : (x) - 'a' + 1)
FOR(i, 0, len1)
x[i] = id(s[i]) * id(s[i]);
FOR(i, 0, len2)
y[i] = id(t[i]);
DFT(x, sz), DFT(y, sz);
FOR(i, 0, sz)
ans[i] = add(ans[i], mul(x[i], y[i]));
memset(x, 0, sizeof x);
memset(y, 0, sizeof y);
FOR(i, 0, len1)
x[i] = 1;
FOR(i, 0, len2)
y[i] = id(t[i]) * id(t[i]) * id(t[i]);
DFT(x, sz), DFT(y, sz);
FOR(i, 0, sz)
ans[i] = add(ans[i], mul(x[i], y[i]));
memset(x, 0, sizeof x);
memset(y, 0, sizeof y);
FOR(i, 0, len1)
x[i] = id(s[i]);
FOR(i, 0, len2)
y[i] = id(t[i]) * id(t[i]);
DFT(x, sz), DFT(y, sz);
FOR(i, 0, sz)
ans[i] = add(ans[i], mul(-2, mul(x[i], y[i])));
IDFT(ans, sz);
int cnt = 0;
For(i, len2 - 1, len1 - 1)
if (!ans[i])
::pos[++ cnt] = i - len2 + 1;
printf("%d\n", cnt);
For(i, 1, cnt)
printf("%d\n", ::pos[i]);
return 0;
}
这个关于字符串相等的转换特别奇妙。