洛谷P4173:残缺的字符串(FFT、通配符匹配)

34 篇文章 0 订阅
13 篇文章 0 订阅

解析

通配符匹配的经典题。
设单词串为 A A A,文章串为 B B B
A A A 翻转一下,判断问题就能转化为一个卷积的形式:
F ( p ) = & i = 0 m − 1 m a t c h ( A i + 1 , B p − i ) F(p)=\&_{i=0}^{m-1}match(A_{i+1},B_{p-i}) F(p)=&i=0m1match(Ai+1,Bpi)
m a t c h ( a , b ) match(a,b) match(a,b) 表示 a , b a,b a,b 两个字符是否互相匹配。
但是这么丑的东西显然难以优化,我们需要更优美的表达形式。
考虑且运算的性质,我们需要一个非法就能检测出非法,那么我们考虑利用平方的非负性,设计一个函数 C ( a , b ) = ( a − b ) 2 C(a,b)=(a-b)^2 C(a,b)=(ab)2,把函数改写成:
F ( p ) = ∑ i = 0 m − 1 C ( A i + 1 , B p − i ) F(p)=\sum_{i=0}^{m-1}C(A_{i+1},B_{p-i}) F(p)=i=0m1C(Ai+1,Bpi)
位置合法当且仅当对应函数值为0,把平方拆开分别算再加起来即可。

现在考虑如何加入通配符。
只要 a , b a,b a,b 中有任意一个是通配符,就应该是合法的。
利用 0 0 0 乘任何数都是 0 0 0 的性质,对于 C C C 函数进行微调:
C ( a , b ) = ( a − b ) 2 j d ( a ) j d ( b ) C(a,b)=(a-b)^2jd(a)jd(b) C(a,b)=(ab)2jd(a)jd(b)
其中 j d ( a ) jd(a) jd(a) 表示字符 a a a 是否不是 *
这样就可以卷起来算了。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
inline ll read() {
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
const int N=2e6+100;
const int mod=998244353;
int n,m,k;
inline ll ksm(ll x,ll k){
	ll res=1;
	while(k){
		if(k&1) res=res*x%mod;
		x=x*x%mod;k>>=1;
	}
	return res;
}
int r[N];
void init(int n,int &lim){
	lim=1;int L=0;
	while(lim<n) lim<<=1,L++;
	for(int i=1;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
}
void NTT(ll *x,int lim,int op){
	for(int i=0;i<lim;i++) if(i<r[i]) swap(x[i],x[r[i]]);
	for(int l=1;l<lim;l<<=1){
		ll w=ksm(3,(mod-1)/(l<<1));if(op==-1) w=ksm(w,mod-2);
		for(int st=0;st<lim;st+=(l<<1)){
			for(ll i=0,now=1;i<l;i++,now=now*w%mod){
				ll u=x[st+i],v=now*x[st+i+l]%mod;
				x[st+i]=u+v>=mod?u+v-mod:u+v;
				x[st+i+l]=u-v<0?u-v+mod:u-v;
			}
		}
	}
	if(op==-1){
		ll ni=ksm(lim,mod-2);
		for(int i=0;i<lim;i++) x[i]=x[i]*ni%mod;
	}
	return;
}
void copy(ll *a,ll *b,int n,int lim){
	assert(n<=lim);
	memcpy(a,b,sizeof(ll)*n);
	fill(a+n,a+lim,0);return;
}
void mul(ll *a,ll *b,ll *c,int n,int m){
	static ll u[N],v[N];
	static int lim;
	//for(int i=0;i<n;i++) printf("%lld ",a[i]);putchar('\n');
	//for(int i=0;i<m;i++) printf("%lld ",b[i]);putchar('\n');
	init(n+m-1,lim);
	copy(u,a,n,lim);
	copy(v,b,m,lim);
	NTT(u,lim,1);NTT(v,lim,1);
	for(int i=0;i<lim;i++) c[i]=u[i]*v[i]%mod;
	NTT(c,lim,-1);
	//for(int i=0;i<n+m-1;i++) printf("%lld ",c[i]);putchar('\n');
	//putchar('\n');
	return;
}
void inv(ll *h,ll *f,int n){
	static ll t1[N],t2[N];
	static int lim;
	if(n==1){
		f[0]=ksm(h[0],mod-2);return;
	}
	inv(h,f,(n+1)>>1);
	init(n<<1,lim);
	fill(f+((n+1)>>1),f+lim,0);
	copy(t1,f,n,lim);copy(t2,h,n,lim);
	NTT(t1,lim,1);NTT(t2,lim,1);
	for(int i=0;i<lim;i++) t1[i]=(2*t1[i]-t1[i]*t1[i]%mod*t2[i]%mod+mod)%mod;
	NTT(t1,lim,-1);
	memcpy(f,t1,sizeof(ll)*n);
	return;
}
//499122177
void Sqrt(ll *h,ll *f,int n){
	static ll t1[N],t2[N];
	static int lim;
	if(n==1){
		f[0]=1;return;
	}
	Sqrt(h,f,(n+1)>>1);
	init(n<<1,lim);
	fill(f+((n+1)>>1),f+lim,0);
	inv(f,t1,n);
	fill(t1+n,t1+lim,0);
	mul(h,t1,t1,n,n);
	copy(t2,f,n,lim);
	NTT(t1,lim,1);NTT(t2,lim,1);
	for(int i=0;i<lim;i++) t1[i]=(t1[i]+t2[i]%mod)*499122177%mod;
	NTT(t1,lim,-1);
	memcpy(f,t1,sizeof(ll)*n);
	return;
}
void dao(ll *h,ll *f,int n){
	static ll t[N];
	static int lim;
	init(n<<1,lim);
	copy(t,h,n,lim);
	f[n-1]=0;
	for(int i=0;i<n-1;i++) f[i]=t[i+1]*(i+1)%mod;
	fill(f+n,f+lim,0);
	return;
}
void jifen(ll *h,ll *f,int n){
	static ll t[N];
	static int lim;
	init(n<<1,lim);
	copy(t,h,n,lim);
	f[0]=0;
	for(int i=1;i<n;i++) f[i]=h[i-1]*ksm(i,mod-2)%mod;
	fill(f+n,f+lim,0);
}
void Ln(ll *h,ll *f,int n){
	static ll t1[N],t2[N];
	static int lim;
	init(n<<1,lim);
	inv(h,t1,n);
	fill(t1+n,t1+lim,0);
	dao(h,t2,n);
	mul(t1,t2,t1,n,n);
	jifen(t1,f,n);
	return;
}
void Exp(ll *h,ll *f,int n){
	static ll t1[N],t2[N],t3[N];
	static int lim;
	if(n==1){
		f[0]=1;return;
	}
	Exp(h,f,(n+1)>>1);
	init(n<<1,lim);
	fill(f+((n+1)>>1),f+lim,0);
	copy(t1,f,n,lim);
	copy(t2,h,n,lim);
	Ln(f,t3,n);fill(t3+n,t3+lim,0);
	NTT(t1,lim,1);NTT(t2,lim,1);NTT(t3,lim,1);
	for(int i=0;i<lim;i++) t1[i]=t1[i]*(1+t2[i]-t3[i]+mod)%mod;
	NTT(t1,lim,-1);
	memcpy(f,t1,sizeof(ll)*n);
	return;
}
ll a[N],b[N],c[N],res[N];
bool x[N],y[N];
char s1[N],s2[N];
int ans[N],tot;
signed main() {
#ifndef ONLINE_JUDGE
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
#endif
//printf("%d\n",sizeof(a1)/1024/1024);
	m=read();n=read();
	scanf(" %s %s",s1+1,s2+1);
	for(int l=1,r=m;l<r;l++,r--) swap(s1[l],s1[r]);
	for(int i=1;i<=m;i++) x[i]=(s1[i]!='*'),s1[i]-='a'-1;
	for(int i=1;i<=n;i++) y[i]=(s2[i]!='*'),s2[i]-='a'-1;
	
	for(int i=1;i<=m;i++) a[i]=s1[i]*s1[i]*x[i];
	for(int i=1;i<=n;i++) b[i]=y[i];
	mul(a,b,c,m+1,n+1);
	for(int i=1;i<=n+1;i++) res[i]+=c[i];
	
	for(int i=1;i<=m;i++) a[i]=x[i];
	for(int i=1;i<=n;i++) b[i]=s2[i]*s2[i]*y[i];
	mul(a,b,c,m+1,n+1);
	for(int i=1;i<=n+1;i++) res[i]+=c[i];
	
	for(int i=1;i<=m;i++) a[i]=s1[i]*x[i];
	for(int i=1;i<=n;i++) b[i]=s2[i]*y[i];
	mul(a,b,c,m+1,n+1);
	for(int i=1;i<=n+1;i++) res[i]-=2*c[i];
	for(int p=m;p<=n;p++) if(res[p+1]==0) ans[++tot]=p-m+1;
	printf("%d\n",tot);
	for(int i=1;i<=tot;i++) printf("%d ",ans[i]);
	return 0;
}
/*
4
0 1 1
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值