残缺的字符串
题目描述
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串 A 和 B ,其中 A 串长度为 m , B 串长度为 n 。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。
你想对这两个串重新进行匹配,其中 A 为模板串,那么现在问题来了,请回答,对于 B 的每一个位置 i,从这个位置开始连续 m 个字符形成的子串是否可能与 A 串完全匹配?
输入输出格式
输入格式:
第一行包含两个正整数 m , n ,分别表示 A 串和 B 串的长度。
第二行为一个长度为 m 的字符串 A 。
第三行为一个长度为 n 的字符串 B 。
两个串均仅由小写字母和号组成,其中号表示相应位置已经残缺。
输出格式:
第一行包含一个整数 k ,表示 B 串中可以完全匹配 A 串的位置个数。
若 k > 0 ,则第二行输出 k 个正整数,从小到大依次输出每个可以匹配的开头位置(下标从 1 开始)。
输入输出样例
输入样例#1:
3 7
a * b
aebr * ob
输出样例#1:
2
1 5
说明
100 %的数据满足 \(n <= m <= 300000\)
震惊,FFT竟然干出这种事
我是找FFT题找到这道题的
但是瞅了半天没看出来咋FFT ==
觉得咋看咋像KMP之类的东西
看了题解深入思考
发现肥肠的妙
然后我们把*的权值设置成0,其他的就随便设个权值就好了
然后我们可以构造一个函数\(E[i]\),表示在B串中结尾为i是否能完整的匹配A串
所以\(E[i] = \sum_{j=1}^{m}{(A[m-j+1]-y[i-j+1])^2A[m-j+1]B[i-j+1]}\)
但是这玩意儿并不能卷积==
所以我们把还是把A串翻转过来
这样\(E[i] = \sum_{j=1}^{m}{(revA[j]-B[i-j+1)^2revA[j]B[i-j+1]}\)
然后我们再把各项都拆开,式子就成了
\(E[i] = \sum_{j=1}^{m}{revA[j]^3B[i-j+1]}-2\sum_{j=1}^{m}{revA[j]^2B[i-j+1]^2}+\sum_{j=1}^{m}{revA[j]B[i-j+1]^3}\)
然后我们就分别对三段卷积就好辣
但是注意卷积后第i+1项代表的是E[i]
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std ;
const int M = 1500005 ;
const double eps = 1e-8 ;
const double Pi = acos(-1.0) ;
struct Complex {
double x , y ;
Complex (double Tx = 0 , double Ty = 0) { x = Tx , y = Ty ;}
}a[M] , b[M] ;
Complex operator + (Complex a , Complex b) {
return Complex (a.x + b.x , a.y + b.y) ;
}
Complex operator - (Complex a , Complex b) {
return Complex (a.x - b.x , a.y - b.y) ;
}
Complex operator * (Complex a , Complex b) {
return Complex (a.x * b.x - a.y * b.y , a.x * b.y + a.y * b.x) ;
}
char s[M] ;
int n , m ;
int l , r[M] , digital = 1 ;
int x[M] , y[M] , tot , pos[M] ;
double Ans[M] ;
inline void FFT(Complex *A , int unit) {
for(int i = 0 ; i < digital ; i ++)
if(r[i] > i)
swap(A[i] , A[r[i]]) ;
for(int mid = 1 ; mid < digital ; (mid <<= 1)) {
Complex W (cos(Pi / mid) , unit * sin(Pi / mid)) ;
int R = (mid << 1) ;
for(int j = 0 ; j < digital ; j += R) {
Complex w(1 , 0) ;
for(int k = 0 ; k < mid ; k ++ , w = w * W) {
Complex x = A[j + k] , y = w * A[j + k + mid] ;
A[j + k] = x + y ; A[j + k + mid] = x - y ;
}
}
}
}
inline void Solve() {
while(digital <= n + m) digital <<= 1 , l ++ ;
for(int i = 0 ; i < digital ; i ++)
r[i] = (r[i>>1]>>1) | ((i&1)<<(l - 1)) ;
FFT(a , 1) ; FFT(b , 1) ;
for(int i = 0 ; i <= digital ; i ++) a[i] = a[i] * b[i] ;
FFT(a , -1) ;
}
inline void Clear() {
memset(a , 0 , sizeof(a)) ;
memset(b , 0 , sizeof(b)) ;
digital = 1 ; l = 0 ;
}
int main() {
scanf("%d%d",&m,&n) ;
scanf("%s",s + 1) ;
for(int i = 1 ; i <= m ; i ++) {
if(s[i] == '*') x[i] = 0 ;
else x[i] = s[i] - 'a' + 1 ;
}
scanf("%s",s + 1) ;
for(int i = 1 ; i <= n ; i ++) {
if(s[i] == '*') y[i] = 0 ;
else y[i] = s[i] - 'a' + 1 ;
}
for(int i = 1 ; i <= (m>>1) ; i ++) swap(x[i] , x[m - i + 1]) ;
++ m ; ++ n ;
for(int i = 1 ; i <= m ; i ++) a[i].x = x[i] * x[i] * x[i] ;
for(int i = 1 ; i <= n ; i ++) b[i].x = y[i] ;
Solve() ;
for(int i = m ; i <= n ; i ++) Ans[i] += (a[i].x / digital) ;
Clear() ;
for(int i = 1 ; i <= m ; i ++) a[i].x = x[i] * x[i] ;
for(int i = 1 ; i <= n ; i ++) b[i].x = y[i] * y[i] ;
Solve() ;
for(int i = m ; i <= n ; i ++) Ans[i] -= 2 * (a[i].x / digital) ;
Clear() ;
for(int i = 1 ; i <= m ; i ++) a[i].x = x[i] ;
for(int i = 1 ; i <= n ; i ++) b[i].x = y[i] * y[i] * y[i] ;
Solve() ;
for(int i = m ; i <= n ; i ++) Ans[i] += (a[i].x / digital) ;
for(int i = m ; i <= n ; i ++)
if(abs(Ans[i]) <= eps)
pos[++tot] = i - m + 1 ;
printf("%d\n",tot) ;
for(int i = 1 ; i <= tot ; i ++) printf("%d ",pos[i]) ;
return 0 ;
}