luogu4173 残缺的字符串

对于一类带有通配符的字符串匹配问题,我们考虑构造匹配函数,通过匹配函数的值来判断匹配的位置。

先考虑一个不带通配符的问题:给定两个字符串\(A,B\),判断\(B\)的哪些位置能与\(A\)匹配。

除了kmp,我们同样可以考虑构造匹配函数来解决匹配问题,首先将\(A\)串翻转同时在其末尾补\(0\),构造函数\(f_i=\sum_{j=0}^i(A_j-B_{i-j})^2\),那么\(B\)中在第\(i\)个位置结尾的长度为\(|A|\)的子串能与\(A\)匹配当且仅当\(f_i=0\)。将函数展开后得到\(\sum_{j=0}^i(A_j^2-2A_jB_{i-j}+B_{i-j}^2)\),只需要一次FFT便能得到结果。

现在考虑有通配符的情况,那么原来对匹配函数的定义显然是不完备的,考虑第\(i\)位有通配符时直接算作匹配,故可以在第\(i\)位为通配符时令\(A_i=0\),那么便可将匹配函数的定义完善为\(f_i=\sum_{j=0}^i(A_j-B_{i-j})^2A_jB_{i-j}\).

展开后得到\(f_i=\sum_{j=0}^i(A_j^3B_{i-j}-2A_j^2B_{i-j}^2+A_jB_{i-j}^3)\).

做三次多项式乘法即可。

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=300000+100;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u];i;i=sq[i].nxt)
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

namespace My_Math{
	#define N 100000

	int fac[N+100],invfac[N+100];

	int add(int x,int y) {return x+y>=maxd?x+y-maxd:x+y;}
	int dec(int x,int y) {return x<y?x-y+maxd:x-y;}
	int mul(int x,int y) {return 1ll*x*y%maxd;}
	ll qpow(ll x,int y)
	{
		ll ans=1;
		while (y)
		{
			if (y&1) ans=mul(ans,x);
			x=mul(x,x);y>>=1;
		}
		return ans;
	}
	int inv(int x) {return qpow(x,maxd-2);}

	int math_init()
	{
		fac[0]=invfac[0]=1;
		rep(i,1,N) fac[i]=mul(fac[i-1],i);
		invfac[N]=inv(fac[N]);
		per(i,N-1,1) invfac[i]=mul(invfac[i+1],i+1);
	}
	#undef N
}
using namespace My_Math;

namespace polynomial{
	struct complex{
		double x,y;
		complex (double _x=0.0,double _y=0.0) {x=_x;y=_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);}
	complex operator *(complex a,complex b) {return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
	
	int r[N<<2];
	void calcr(int &lim,int len)
	{
		lim=1;int cnt=0;
		while (lim<len) {lim<<=1;cnt++;}
		rep(i,0,lim-1) 
			r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));
	}
	
	void fft(int lim,complex *a,int typ)
	{
		rep(i,0,lim-1)
			if (i<r[i]) swap(a[i],a[r[i]]);
		for (int mid=1;mid<lim;mid<<=1)
		{
			complex wn=complex(cos(pi/mid),sin(pi/mid)*typ);
			int len=(mid<<1);
			for (int sta=0;sta<lim;sta+=len)
			{
				complex w=complex(1,0);
				for (int j=0;j<mid;j++,w=w*wn)
				{
					complex x=a[j+sta],y=a[j+sta+mid]*w;
					a[j+sta]=x+y;a[j+sta+mid]=x-y;
				}
			}
		}
		if (typ==-1)
			rep(i,0,lim-1) a[i].x/=lim;
	}
}
using namespace polynomial;
complex A[N<<2],B[N<<2],C[N<<2],emp=complex(0,0);
int a[N],b[N],n,m;
char s[N];
vector<int> ans;

int main()
{
	n=read();m=read();
	scanf("%s",s);
	rep(i,0,n-1) 
		if (s[i]!='*') a[i]=s[i]-'a'+1;
	reverse(a,a+n);
	scanf("%s",s);
	rep(i,0,m-1) 
		if (s[i]!='*') b[i]=s[i]-'a'+1;
	int lim=0;
	calcr(lim,m*2);
	rep(i,0,n-1) A[i]=complex(a[i]*a[i]*a[i],0);
	rep(i,0,m-1) B[i]=complex(b[i],0);
	fft(lim,A,1);fft(lim,B,1);
	rep(i,0,lim-1) C[i]=C[i]+A[i]*B[i];
	rep(i,0,lim-1) A[i]=B[i]=emp;
	rep(i,0,n-1) A[i]=complex(a[i]*a[i],0);
	rep(i,0,m-1) B[i]=complex(b[i]*b[i],0);
	fft(lim,A,1);fft(lim,B,1);
	rep(i,0,lim-1) C[i]=C[i]-A[i]*B[i]*2;
	rep(i,0,lim-1) A[i]=B[i]=emp;
	rep(i,0,n-1) A[i]=complex(a[i],0);
	rep(i,0,m-1) B[i]=complex(b[i]*b[i]*b[i],0);
	fft(lim,A,1);fft(lim,B,1);
	rep(i,0,lim-1) C[i]=C[i]+A[i]*B[i];
	fft(lim,C,-1);
	rep(i,n-1,m-1)
		if (fabs(C[i].x)+0.5<1) ans.pb(i-n+2);
	int len=ans.size();
	printf("%d\n",len);
	rep(i,0,len-1) printf("%d ",ans[i]);
	return 0;
}
posted @ 2020-02-29 23:48  EncodeTalker  阅读(99)  评论(0编辑  收藏  举报