VEXOBEN
Vexoben
Jun 3, 2018
It takes 5 minutes to read this article.

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4259

(刷新以获取数学公式)

题意

给定带通配符的两串,求中的出现位置。

题解

下面认为的下标范围是的下标范围是

通配符的出现使kmp失去了效果。在不考虑通配符的情况下,用另一种方式思考字符串匹配。

,那么在位置能匹配,当且仅当:

下标差为定值不能直接用FFT计算卷积,翻转使和变为定值。

,则:

三部分计算完成后相加即可,则匹配。

那么考虑通配符呢?

如果是通配符,令同理,那么只需即可

由于翻转的原因,如果,实际上是在的位置匹配。原题要求下标范围从1开始,那么再加1即可。

#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20)+10;
const double pi=acos(-1.0);

char s[N],t[N];
int n,m,cnt,rev[N];
struct cp {
	double x,y;
	cp (double a,double b) {x=a,y=b;}
	cp (){}
	cp operator + (cp t) {return cp(x+t.x,y+t.y);}
	cp operator - (cp t) {return cp(x-t.x,y-t.y);}
	cp operator * (cp t) {return cp(x*t.x-y*t.y,x*t.y+y*t.x);}
}a[N],b[N],c[N],d[N],ans[N];

void FFT(cp *a,int limit,int type) {
	for (int i=0;i<=limit;i++)	if (i<rev[i]) swap(a[i],a[rev[i]]);
	for (int len=2;len<=limit;len<<=1) {
		int mid=len>>1;
		cp wn(cos(2*pi/len),type*sin(2*pi/len));
		for (int i=0;i<limit;i+=len) {
			cp w(1,0);
			for (int j=i;j<i+mid;j++) {
				cp x=a[j],y=w*a[j+mid];
				a[j]=x+y; a[j+mid]=x-y;	w=w*wn;
			}
		}
	}
	if (type==-1) for (int i=0;i<=limit;i++) a[i].x/=limit;
}

void conv(cp *a,cp *b,int limit,int link) {
	FFT(a,limit,1); FFT(b,limit,1);
	for (int i=0;i<=limit;i++) a[i]=a[i]*b[i];
	for (int i=0;i<=limit;i++) ans[i].x+=a[i].x*link,ans[i].y+=a[i].y*link;
}

int main() {
	scanf("%d%d",&n,&m);	n--; m--;
	scanf("%s%s",t,s);
   int limit=1,l=0;
	while (limit<=n+m) l++,limit<<=1;
	for (int i=0;i<=limit;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);	
	for (int i=0;i<=m;i++) a[i].x=(s[i]=='*')?0:s[i]-'a'+1;
	for (int i=0;i<=n;i++) b[n-i].x=(t[i]=='*')?0:t[i]-'a'+1;
	for (int i=0;i<=limit;i++) c[i]=a[i]*a[i]*a[i],d[i]=b[i];
	conv(c,d,limit,1); 
	for (int i=0;i<=limit;i++) c[i]=a[i]*a[i],d[i]=b[i]*b[i];
	conv(c,d,limit,-2);
	for (int i=0;i<=limit;i++) c[i]=a[i],d[i]=b[i]*b[i]*b[i];
	conv(c,d,limit,1);
	FFT(ans,limit,-1);
	for (int i=n;i<=m;i++) if (!(int)(ans[i].x+0.5)) cnt++;
	printf("%d\n",cnt);
	for (int i=n;i<=m;i++) if (!(int)(ans[i].x+0.5)) printf("%d ",i-n+1);
	return 0;
}