牛客網暑期ACM多校訓練營(第三場)D 字串匹配 FFT
阿新 • • 發佈:2018-12-14
題意:給定兩個串A,B,問A中有多少個長度等於B的子串與B相似,兩個同樣長度的字串相似的條件是:對於對應位置上的字元,差的絕對值不大於1
思路: 可以類似於FFT用於字串匹配的演算法。 假設的長度為,的長度為,且下標均從1開始 則相似子串的個數:
將中間的式子展開:
前面的四項可以處理一個字首和,而後面的四項可以通過反轉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;
}