1. 程式人生 > >牛客網暑期ACM多校訓練營(第三場)D 字串匹配 FFT

牛客網暑期ACM多校訓練營(第三場)D 字串匹配 FFT

題目連結

題意:給定兩個串A,B,問A中有多少個長度等於B的子串與B相似,兩個同樣長度的字串相似的條件是:對於對應位置上的字元,差的絕對值不大於1

思路: 可以類似於FFT用於字串匹配的演算法。 假設AA的長度為mmBB的長度為nn,且下標均從1開始 則相似子串的個數:

Ans=j=0m1[i=1n(Ai+jBi)2((Ai+jBi)21)=0]Ans = \sum_{j=0}^{m-1}[\sum_{i=1}^{n}(A_{i+j}-B_i)^2*((A_{i+j}-B_i)^2 - 1) = 0]

Bi)21)=0]

將中間的式子展開: (Ai+jBi)2((Ai+jBi)21)(A_{i+j}-B_i)^2*((A_{i+j}-B_i)^2 - 1)

=(Ai+j2+Bi22Ai+jBi)(Ai+j2+Bi22Ai+jBi1)=(A_{i+j}^2+B_i^2-2A_{i+j}B_i)*(A_{i+j}^2+B_i^2-2A_{i+j}B_i - 1) =(Ai+j4Ai+j2)+(Bi4Bi2)+6Ai

+j2Bi2+2Ai+jBi4Ai+j3Bi4Ai+jBi3=(A_{i+j}^4-A_{i+j}^2)+(B_i^4-B_i^2)+6A_{i+j}^2B_i^2+2A_{i+j}B_i-4A_{i+j}^3B_i-4A_{i+j}B_i^3

前面的四項可以處理一個字首和,而後面的四項可以通過反轉B以後進行卷積,FFT優化。

隨後就可以列舉每個位置,O(1)判斷了

以上。

程式碼:

#include<cstdio>
#include
<cstring>
#include<cmath> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const double pi = acos(-1.0); const int A = 250000 + 10; const int B = A<<3; char S[A], T[A], E[30]; int n, m, tot, len, Ans[A]; ll sumx[A], sumy[A]; struct comp{ double r, i; comp(double _r = 0, double _i = 0) {r = _r; i = _i;} comp operator + (const comp x){return comp(r+x.r, i+x.i);} comp operator - (const comp x){return comp(r-x.r, i-x.i);} comp operator * (const comp x){return comp(r*x.r-i*x.i, r*x.i+i*x.r);} comp operator * (const int x){return comp(r*x, i*x);} }X[4][B], Y[3][B], Z[B]; void FFT(comp a[], int n, int t){ for (int i = 1, j = 0; i < n - 1; i++) { for (int s = n; j ^= s>>=1, ~j&s;); if (i < j) swap(a[i], a[j]); } for (int d = 0; (1<<d) < n; d++) { int m = 1<<d, m2 = m<<1; double o = pi/m*t; comp _w(cos(o), sin(o)); for (int i = 0; i < n; i += m2) { comp w(1, 0); for (int j = 0; j < m; j++) { comp &A = a[i+j+m], &B = a[i+j], t = w*A; A = B - t; B = B + t; w = w * _w; } } } if (t == -1) for(int i = 0; i < n; i++) a[i].r /= n; } void Init(){ len = 1; while (len < m+2) len <<= 1; sumx[0] = sumy[0] = 0; for (int i = 1; i <= n; i++) { int x = S[i] - 'a' + 1; sumx[i] = sumx[i-1] + (x*x*x*x - x*x); X[0][n-i+1] = comp(2.0*x, 0); X[1][n-i+1] = comp(6.0*x*x, 0); X[2][n-i+1] = comp(4.0*x*x*x, 0); X[3][n-i+1] = comp(4.0*x, 0); } for (int i = 1; i <= m; i++) { int y = T[i] - 'a' + 1; sumy[i] = sumy[i-1] + (y*y*y*y - y*y); Y[0][i] = comp(1.0*y, 0); Y[1][i] = comp(1.0*y*y, 0); Y[2][i] = comp(1.0*y*y*y, 0); } } void calc(){ Init(); for (int i = 0; i < 3; i++) { FFT(X[i], len, 1); FFT(Y[i], len, 1); } FFT(X[3], len, 1); for (int i = 0; i < len; i++) Z[i] = X[1][i]*Y[1][i] + X[0][i]*Y[0][i] - X[2][i]*Y[0][i] - X[3][i]*Y[2][i]; FFT(Z, len, -1); tot = 0; for (int j = 0; j <= m - n; j++) { if ((ll)(Z[n+j+1].r + sumx[n] + sumy[n+j] - sumy[j]) == 0) Ans[++tot] = j + 1; } printf("%d\n", tot); for (int i = 1; i <= tot; i++) printf("%d%c", Ans[i], i==tot?'\n':' '); } int main(){ scanf("%s%s%s", S + 1, T + 1, E); n = strlen(S + 1); m = strlen(T + 1); for (int i = 1; i <= n; i++) S[i] = E[S[i]-'a']; for (int i = 1; i <= m; i++) T[i] = E[T[i]-'a']; calc(); return 0; }