#FFT#P4173 残缺的字符串

题目

有一个长度为 \(m\) 的字符串 \(A\) 和 一个长度为 \(n\) 的字符串 \(B\)

它们有若干位置为通配符,现在问 \(B\) 的每一个后缀是否存在前缀为 \(A\)


分析

考虑每次就是问 \(A[m-i]\)\(B[r-i+1]\) 是否相等,把 \(A\) 反转一下,就得到 \(A'[i]\)

然后建立一个匹配函数 \((A[i]-B[j])^2A[i]B[j]\) 一定要乘起来之后都为0,这个后缀才有可能。

然后NTT好像会被卡,所以写了FFT


代码

#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#include <algorithm>
#define rr register
#define mem(f,n) fill(f,f+n,(CP){0,0})
#define cpy(f,g,n) memcpy(f,g,sizeof(CP)*(n))
using namespace std;
const double pi=acos(-1.0);
const int N=2000011; int n,m,len,a[N],b[N],stac[N],TOP;
double Sin[31],Cos[31];
struct CP{
	double x,y;
	inline CP operator +(const CP &t)const{return (CP){x+t.x,y+t.y};}
	inline CP operator -(const CP &t)const{return (CP){x-t.x,y-t.y};}
	inline CP operator *(const CP &t)const{return (CP){x*t.x-y*t.y,x*t.y+y*t.x};}
}ff[N<<2],ans[N<<2];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void print(int ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
namespace Fourier{
	int rev[N<<2],LAST; CP Wt[N<<2],F[N<<2];
	inline void Pro(int n){
		if (LAST==n) return; LAST=n,Wt[0]=(CP){1,0};
		for (rr int i=0;i<n;++i)
		    rev[i]=(rev[i>>1]>>1)|((i&1)?n>>1:0);
	}
	inline void FFT(CP *f,int n,int op){
		Pro(n);
		for (rr int i=0;i<n;++i) F[i]=f[rev[i]];
	    for (rr int o=1,len=1;len<n;++o,len<<=1){
	    	rr CP W=(CP){Cos[o-1],(op==1)?Sin[o-1]:-Sin[o-1]};
	    	for (rr int j=1;j<len;++j) Wt[j]=Wt[j-1]*W;
	    	for (rr int i=0;i<n;i+=len+len)
	    	for (rr int j=0;j<len;++j){
	    		rr CP t=Wt[j]*F[i|j|len];
	    		F[i|j|len]=F[i|j]-t,F[i|j]=F[i|j]+t;
			}
		}
		for (rr int i=0;i<n;++i) f[i]=F[i];
    }
	inline void Cb(CP *f,CP *g,int n){
		for (rr int i=0;i<n;++i) f[i]=f[i]*g[i];
	}
}
inline void CosSin(){
	for (rr int i=0;i<31;++i) Cos[i]=cos(pi/(1<<i));
    for (rr int i=0;i<31;++i) Sin[i]=sin(pi/(1<<i));		
}
signed main(){
    m=iut(),n=iut(),CosSin();
    for (rr int i=0;i<m;++i){
    	rr char ch=getchar();
    	while (!isalpha(ch)&&ch!='*') ch=getchar();
		if (ch!='*') a[i]=ch-96; 
	}
    for (rr int i=0;i<n;++i){
    	rr char ch=getchar();
    	while (!isalpha(ch)&&ch!='*') ch=getchar();
		if (ch!='*') b[i]=ch-96; 
	}
	reverse(a,a+m); for (len=1;len<n+n;len<<=1);
	for (rr int i=0;i<len;++i) ff[i]=(CP){a[i]*a[i]*a[i]/676.0,b[i]*1.0};
    Fourier::FFT(ff,len,1),Fourier::Cb(ff,ff,len);
	for (rr int i=0;i<len;++i) ans[i].x+=ff[i].y/2;
	for (rr int i=0;i<len;++i) ff[i]=(CP){a[i]*1.0,b[i]*b[i]*b[i]/676.0};
    Fourier::FFT(ff,len,1),Fourier::Cb(ff,ff,len);
	for (rr int i=0;i<len;++i) ans[i].x+=ff[i].y/2;
	for (rr int i=0;i<len;++i) ff[i]=(CP){a[i]*a[i]/26.0,b[i]*b[i]/26.0};
    Fourier::FFT(ff,len,1),Fourier::Cb(ff,ff,len);
	for (rr int i=0;i<len;++i) ans[i].x-=ff[i].y;
	Fourier::FFT(ans,len,-1);
	for (rr int i=0;i<len;++i) ans[i].x=ans[i].x*676.0/len;
	for (rr int i=m-1;i<n;++i) if (fabs(ans[i].x)<0.5) stac[++TOP]=i-m+2;
	print(TOP);
	for (rr int i=1;i<=TOP;++i)
	    putchar(i==1?10:32),print(stac[i]);
    return 0;
}
posted @ 2022-03-10 21:13  lemondinosaur  阅读(27)  评论(0编辑  收藏  举报