【BZOJ4259】—残缺的字符串(FFT)

BZOJ题面link

这套路……真是绝了

发现这个 ∗ * 导致我们无法使用相关的字符串算法

定义差异函数 f A , B = ∑ i = 0 n − 1 ( A [ i ] − B [ i ] ) 2 A [ i ] ∗ B [ i ] f_{A,B}=\sum_{i=0}^{n-1}(A[i]-B[i])^2A[i]*B[i] fA,B=i=0n1(A[i]B[i])2A[i]B[i]
可以发现只有当 A = B A=B A=B f = 0 f=0 f=0
∗ = 0 *=0 =0则问题就能完美转化成判断 f = 0 f=0 f=0

考虑将 A A A翻转并补 0 0 0 n n n
则对于第 i i i位结尾的子串 f = ∑ j = 0 i ( A [ j ] − B [ i − j ] ) 2 A [ i ] ∗ B [ i − j ] f=\sum_{j=0}^{i}(A[j]-B[i-j])^2A[i]*B[i-j] f=j=0i(A[j]B[ij])2A[i]B[ij]
= ∑ j = 0 i A [ i ] 3 B [ i − j ] − 2 ∑ j = 0 i A [ i ] 2 B [ i ] 2 + ∑ j = 0 i A [ i ] ∗ B [ i ] 3 =\sum_{j=0}^{i}A[i]^3B[i-j]-2\sum_{j=0}^{i}A[i]^2B[i]^2+\sum_{j=0}^{i}A[i]*B[i]^3 =j=0iA[i]3B[ij]2j=0iA[i]2B[i]2+j=0iA[i]B[i]3

分别对于三个式子求一下卷积看每一位和是否为 0 0 0就是了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=300005;
const double pi=acos(-1);
struct plx{
	double x,y;
	plx(double _x=0,double _y=0):x(_x),y(_y){}
	friend inline plx operator +(const plx &a,const plx &b){
		return plx(a.x+b.x,a.y+b.y);
	}
	friend inline plx operator -(const plx &a,const plx &b){
		return plx(a.x-b.x,a.y-b.y);
	}
	friend inline plx operator *(const plx &a,const plx &b){
		return plx(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
	}
}a[N<<2],b[N<<2];
int lim=1,tim,rev[N<<2];
inline void fft(plx f[],int kd){
	for(int i=0;i<lim;i++)if(i<rev[i])swap(f[i],f[rev[i]]);
	for(int mid=1;mid<lim;mid<<=1){
		plx now=plx(cos(pi/mid),kd*sin(pi/mid));
		for(int i=0;i<lim;i+=(mid<<1)){
			plx w=plx(1,0);
			for(int j=0;j<mid;j++,w=w*now){
				plx a0=f[i+j],a1=w*f[i+j+mid];
				f[i+j]=a0+a1,f[i+j+mid]=a0-a1;
			}
		}
	}
	if(kd==-1)for(int i=0;i<lim;i++)f[i].x/=lim;
}
inline void clear(){
	for(int i=0;i<lim;i++)a[i].x=a[i].y=b[i].x=b[i].y=0;
}
int n,m,x[N],y[N];
ll ans[N],res[N],tot;
char A[N],B[N];
int main(){
	m=read(),n=read();
	scanf("%s",A),scanf("%s",B);
	while(lim<=n*2)lim<<=1,tim++;
	for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(tim-1));
	for(int i=0;i<m;i++)if(A[i]!='*')x[i]=A[i]-'a'+1;
	for(int i=0;i<n;i++)if(B[i]!='*')y[i]=B[i]-'a'+1;
	reverse(x,x+m);
	for(int i=0;i<n;i++)a[i].x=x[i]*x[i]*x[i],b[i].x=y[i];
	fft(a,1),fft(b,1);for(int i=0;i<lim;i++)a[i]=a[i]*b[i];
	fft(a,-1);for(int i=0;i<=n;i++)ans[i]+=(ll)(a[i].x+0.5);
	clear();
	for(int i=0;i<n;i++)a[i].x=x[i]*x[i],b[i].x=y[i]*y[i];
	fft(a,1),fft(b,1);for(int i=0;i<lim;i++)a[i]=a[i]*b[i];
	fft(a,-1);for(int i=0;i<=n;i++)ans[i]-=2ll*(ll)(a[i].x+0.5);
	clear();
	for(int i=0;i<n;i++)a[i].x=x[i],b[i].x=y[i]*y[i]*y[i];
	fft(a,1),fft(b,1);for(int i=0;i<lim;i++)a[i]=a[i]*b[i];
	fft(a,-1);for(int i=0;i<=n;i++)ans[i]+=(ll)(a[i].x+0.5);
	for(int i=m-1;i<n;i++)if(!ans[i])res[++tot]=i-m+2;
	cout<<tot<<'\n';
	for(int i=1;i<=tot;i++)cout<<res[i]<<" ";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值