题意:给定两个串A,B,问A中有多少个长度等于B的子串与B相似,两个同样长度的字符串相似的条件是:对于对应位置上的字符,差的绝对值不大于1
思路:
可以类似于FFT用于字符串匹配的算法。
假设
A
A
A的长度为
m
m
m,
B
B
B的长度为
n
n
n,且下标均从1开始
则相似子串的个数:
A n s = ∑ j = 0 m − 1 [ ∑ i = 1 n ( A i + j − B i ) 2 ∗ ( ( A i + j − B i ) 2 − 1 ) = 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] Ans=j=0∑m−1[i=1∑n(Ai+j−Bi)2∗((Ai+j−Bi)2−1)=0]
将中间的式子展开:
(
A
i
+
j
−
B
i
)
2
∗
(
(
A
i
+
j
−
B
i
)
2
−
1
)
(A_{i+j}-B_i)^2*((A_{i+j}-B_i)^2 - 1)
(Ai+j−Bi)2∗((Ai+j−Bi)2−1)
=
(
A
i
+
j
2
+
B
i
2
−
2
A
i
+
j
B
i
)
∗
(
A
i
+
j
2
+
B
i
2
−
2
A
i
+
j
B
i
−
1
)
=(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+j2+Bi2−2Ai+jBi)∗(Ai+j2+Bi2−2Ai+jBi−1)
=
(
A
i
+
j
4
−
A
i
+
j
2
)
+
(
B
i
4
−
B
i
2
)
+
6
A
i
+
j
2
B
i
2
+
2
A
i
+
j
B
i
−
4
A
i
+
j
3
B
i
−
4
A
i
+
j
B
i
3
=(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
=(Ai+j4−Ai+j2)+(Bi4−Bi2)+6Ai+j2Bi2+2Ai+jBi−4Ai+j3Bi−4Ai+jBi3
前面的四项可以处理一个前缀和,而后面的四项可以通过反转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;
}