【题目链接】
【前置技能】
- FFT/NTT
【题解】
- 字符串中出现了通配符,一般的字符串算法就失去效果了。
- 先忽略通配符的问题。得到一种算法:令每个位置 Ak=∑i=0LenT−1(Si+k−LenT+1−Ti) A k = ∑ i = 0 L e n T − 1 ( S i + k − L e n T + 1 − T i ) ,若 Ak=0 A k = 0 ,则说明 T T 与匹配。但发现这会出错,相减项有可能会正负抵消,如字符串ab和ba就会被认为是匹配的。得到了一种解决方法:将求和中的减法变成相减之后平方就可以避免抵消的问题了。
- 把T整个串翻转一下,然后把平方展开,发现这是卷积的形式。那么通配符的问题也很好想明白了,在式子上再乘上一个 Ti T i ,其中通配符的值为零即可。
- 最后的式子: Ak=∑i=0k(Tk−i∗S2i)−2∗∑i=0k(T2k−i∗Si)+∑i=0kT3k−i A k = ∑ i = 0 k ( T k − i ∗ S i 2 ) − 2 ∗ ∑ i = 0 k ( T k − i 2 ∗ S i ) + ∑ i = 0 k T k − i 3 。
- 时间复杂度 O(NlogN) O ( N l o g N )
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 262150
#define EPS 1e-3
using namespace std;
const double PI = acos(-1);
char s[MAXN], t[MAXN];
int n, LOG, ls, lt, rev[MAXN];
struct dot{double x, y;}ans[MAXN], a[MAXN], b[MAXN];
dot operator + (dot a, dot b){return (dot){a.x + b.x, a.y + b.y};}
dot operator - (dot a, dot b){return (dot){a.x - b.x, a.y - b.y};}
dot operator * (dot a, dot b){return (dot){a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x};}
dot operator / (dot a, double k){return (dot){a.x / k, a.y / k};}
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
void fft_init(){
rev[0] = 0;
for (int i = 1; i < n; ++i)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (LOG - 1));
}
void fft(dot *a, int opt){
for (int i = 0; i < n; ++i)
if (rev[i] < i) swap(a[rev[i]], a[i]);
for (int len = 2; len <= n; len <<= 1){
dot delta = (dot){cos(PI * 2 / len), sin(PI * 2 / len * opt)};
for (int i = 0; i < n; i += len){
dot cur = (dot){1, 0};
for (int j = i, k = i + len / 2; k < i + len; ++j, ++k){
dot tmp = a[j], tnp = a[k] * cur;
a[j] = tmp + tnp;
a[k] = tmp - tnp;
cur = cur * delta;
}
}
}
if (opt == -1){
for (int i = 0; i < n; ++i)
a[i] = a[i] / n;
}
}
bool sam(double x, double y){
if (x > y - EPS && x < y + EPS) return 1;
return 0;
}
int id(char ch){
if (ch == '?' || ch == '\0') return 0;
else return (ch - 'a' + 1);
}
int main(){
scanf("%s", s);
scanf("%s", t);
ls = strlen(s); lt = strlen(t);
reverse(t, t + lt);
n = 1, LOG = 0;
while (n <= ls + lt) n <<= 1, ++LOG;
fft_init();
for (int i = 0; i < n; ++i)
a[i].x = id(t[i]), a[i].y = 0,
b[i].x = id(s[i]), b[i].y = 0, b[i] = b[i] * b[i];
for (int i = 0; i < n; ++i)
ans[i] = a[i] * a[i] * a[i];
for (int i = 1; i < n; ++i)
ans[i] = ans[i] + ans[i - 1];
fft(a, 1); fft(b, 1);
for (int i = 0; i < n; ++i)
a[i] = a[i] * b[i];
fft(a, -1);
for (int i = 0; i < n; ++i)
ans[i] = ans[i] + a[i];
for (int i = 0; i < n; ++i)
a[i].x = id(t[i]), a[i].y = 0, a[i] = a[i] * a[i],
b[i].x = id(s[i]), b[i].y = 0;
fft(a, 1); fft(b, 1);
for (int i = 0; i < n; ++i)
a[i] = a[i] * b[i];
fft(a, -1);
for (int i = 0; i < n; ++i)
ans[i] = ans[i] - (a[i] / 0.5);
int cnt = 0;
for (int i = lt - 1; i < ls; ++i)
if (sam(ans[i].x, 0)) ++cnt;
printf("%d\n", cnt);
for (int i = lt - 1; i < ls; ++i)
if (sam(ans[i].x, 0)) printf("%d\n", i - lt + 1);
return 0;
}