【BZOJ4259】 殘缺的字串
阿新 • • 發佈:2019-01-24
【題目連結】
【前置技能】
- FFT/NTT
【題解】
- 字串中出現了萬用字元,一般的字串演算法就失去效果了。
- 先忽略萬用字元的問題。令每個位置,若,則說明與匹配。但發現這可能會出錯,相減項有可能會正負抵消,如字串ab和ba就會被認為是匹配的。得到一種解決方法:將求和中的減法變成相減之後平方就可以避免抵消的問題了。
- 把模式串翻轉一下,把平方展開,發現是卷積的形式。那麼萬用字元的問題也很好想明白了,在式子上再乘上
- 最後的式子:。
- 如果FFT精度處理得很不好的話,有可能會過不去。
- 時間複雜度
【程式碼】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXS 300010
#define MAXN 1048577
#define eps 1
using namespace std;
const double pi = acos(-1);
int n, m, s[MAXN], t[MAXN], cnt;
char S[MAXS], T[MAXS];
int N, LOG, rev[MAXN];
struct dot{double x, y;}A[MAXN], B[MAXN], C[MAXN], tmp[MAXN], tnp[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(){
N = 1, LOG = 0;
while (N < n + m) N <<= 1, ++LOG;
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, t = i + len / 2; t < i + len; ++j, ++t){
dot tmp = a[j], tnp = a[t] * cur;
a[j] = tmp + tnp, a[t] = tmp - tnp;
cur = cur * delta;
}
}
}
if (opt == -1){
for (int i = 0; i < N; ++i)
a[i] = a[i] / (1.0 * N);
}
}
int id(char ch){
if (ch == '*') return 0;
else return (ch - 'a' + 1);
}
bool sam(double a, double b){
return (a >= b - eps && a <= b + eps);
}
int main(){
read(n), read(m);
scanf("%s", S);
scanf("%s", T);
for (int i = 0; i < n; ++i)
s[i] = id(S[n - i - 1]);
for (int i = 0; i < m; ++i)
t[i] = id(T[i]);
fft_init();
for (int i = 0; i < N; ++i)
tmp[i].x = s[i] * s[i] * s[i], tnp[i].x = t[i], tmp[i].y = 0, tnp[i].y = 0;
fft(tmp, 1), fft(tnp, 1);
for (int i = 0; i < N; ++i)
A[i] = tmp[i] * tnp[i];
for (int i = 0; i < N; ++i)
tmp[i].x = s[i] * s[i], tnp[i].x = t[i] * t[i], tmp[i].y = 0, tnp[i].y = 0;
fft(tmp, 1), fft(tnp, 1);
for (int i = 0; i < N; ++i)
B[i] = tmp[i] * tnp[i];
for (int i = 0; i < N; ++i)
tmp[i].x = s[i], tnp[i].x = t[i] * t[i] * t[i], tmp[i].y = 0, tnp[i].y = 0;
fft(tmp, 1), fft(tnp, 1);
for (int i = 0; i < N; ++i)
C[i] = tmp[i] * tnp[i];
fft(A, -1), fft(B, -1), fft(C, -1);
for (int i = 0; i < N; ++i)
A[i] = A[i] - (B[i] / 0.5) + C[i];
for (int i = n - 1; i < m; ++i)
if (sam(A[i].x, 0)) ++cnt;
printf("%d\n", cnt);
int fla = 0;
for (int i = n - 1; i < m; ++i)
if (sam(A[i].x, 0)) {
if (fla) printf(" ");
if (!fla) fla = 1;
printf("%d", i - (n - 2));
}
if (cnt != 0) printf("\n");
return 0;
}