1. 程式人生 > >【BZOJ4259】 殘缺的字串

【BZOJ4259】 殘缺的字串

【題目連結】

【前置技能】

  • FFT/NTT

【題解】

  • 字串中出現了萬用字元,一般的字串演算法就失去效果了。
  • 先忽略萬用字元的問題。令每個位置Ak=i=0LenT1(Si+kLenT+1Ti),若Ak=0,則說明TS[kLenT+1,k]匹配。但發現這可能會出錯,相減項有可能會正負抵消,如字串ab和ba就會被認為是匹配的。得到一種解決方法:將求和中的減法變成相減之後平方就可以避免抵消的問題了。
  • 把模式串翻轉一下,把平方展開,發現是卷積的形式。那麼萬用字元的問題也很好想明白了,在式子上再乘上
    TiSi
    ,其中萬用字元的值為零即可。
  • 最後的式子:Ak=i=0k(TkiSi3)2i=0k(Tki2Si2)+i=0k(Tki3Si)
  • 如果FFT精度處理得很不好的話,有可能會過不去。
  • 時間複雜度O(NlogN)

【程式碼】

#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; }