bzoj4259 残缺的字符串(dp+FFT)
2018-03-01 17:04
274 查看
Description
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。你想对这两个串重新进行匹配,其中A为模板串,那么现在问题来了,请回答,对于B的每一个位置i,从这个位置开始连续m个字符形成的子串是否可能与A串完全匹配?
Input
第一行包含两个正整数m,n(1<=m<=n<=300000),分别表示A串和B串的长度。第二行为一个长度为m的字符串A。
第三行为一个长度为n的字符串B。
两个串均仅由小写字母和*号组成,其中*号表示相应位置已经残缺。
Output
第一行包含一个整数k,表示B串中可以完全匹配A串的位置个数。若k>0,则第二行输出k个正整数,从小到大依次输出每个可以匹配的开头位置(下标从1开始)。
Sample Input
3 7a*b
aebr*ob
Sample Output
21 5
HINT
Source
By Claris[Submit][Status][Discuss]
分析:
毒瘤Claris!
我要写出最好的题解首先我们要需要一个东西来判断两个字符串是否相等
设dis(A,B)dis(A,B)表示字符串AA和BB的差异程度(假设AA和BB的长度相等为nn)
dis(A,B)=∑n−1i=0(Ai−Bi)2dis(A,B)=∑i=0n−1(Ai−Bi)2
由于字符串中存在通配字符,所以我们把“∗”“∗”的价值设为0,并且更改一下disdis的表达式:
dis(A,B)=∑n−1i=0AiBi(Ai−Bi)2dis(A,B)=∑i=0n−1AiBi(Ai−Bi)2
由题目给出的数据范围可以看出,BB串较长
所以我们可以枚举BB串中匹配结束的点ii
设计函数:
fi=dis(A,B[i−m+1,i])=∑m−1j=0AjBi−m+1+j(Aj−Bi−m+1+j)2fi=dis(A,B[i−m+1,i])=∑j=0m−1AjBi−m+1+j(Aj−Bi−m+1+j)2
当且仅当fi=0fi=0时,两字符串相等
上面这个式子很像卷积,然而仅仅是像而已
我们能不能把ta构造成一个卷积的形式呢? 答案是肯定的
观察一下卷积的定义式:ck=∑ni=0aibk−ick=∑i=0naibk−i
a,ba,b的下标之和是一个定值
我们在字符串匹配的时候,从第一位开始:
如果我们把第二个字符串翻转一下:
就可以发现,AA和BB的比对位置相加是一个定值,符合卷积的形式了
fi=dis(A,B[i−m+1,i])=∑m−1j=0Am−1−jBi−m+1+j(Am−1−j−Bi−m+1+j)2fi=dis(A,B[i−m+1,i])=∑j=0m−1Am−1−jBi−m+1+j(Am−1−j−Bi−m+1+j)2
B的下标实在太复杂了
为了简化下标,但是我们可以在A后面添加很多个0,让ta变成一个枚举i的式子
(意会一下就可以了)
fi=∑m−1j=0AjBi−j(Aj−Bi−j)2fi=∑j=0m−1AjBi−j(Aj−Bi−j)2
fi=∑m−1j=0A3jBi−j−2A2jB2i−j+AjB3i−jfi=∑j=0m−1Aj3Bi−j−2Aj2Bi−j2+AjBi−j3
上式的三个部分都是卷积的形式
对于形如A3jAj3的部分,直接Aj∗Aj∗AjAj∗Aj∗Aj即可(因为下标都相等,并不是卷积,只是最简单的乘法)
我们将三个部分分别计算卷积,相加即可
因为我们枚举的是BB串中匹配结束的点ii
如果fi=0fi=0,那么匹配起点是i−m+1i−m+1
Q.
为什么卷积可以完成匹配的任务呢?我们不是应该枚举BB的每一个字符吗?
A.
想一下卷积是干什么用的:多项式乘法也就是说,A∗BA∗B,BB的每一个位置都会乘上AA的第ii位
这就相当于以BB的每一位置为匹配起点进行匹配
tip
数组的大小都是fnfn(大于n+m最小二的整数幂)对于aa和bb的赋值不要偷懒只改xx
#include<bits/stdc++.h> using namespace std; const int N=1000010; const double pi=acos(-1.0); struct node{ double x,y; node (double xx=0,double yy=0) { x=xx;y=yy; } }; node a ,b ,c ,o ,_o ; int n,m,fn,ans[N>>1]; node operator +(const node &a,const node &b) {return node(a.x+b.x,a.y+b.y);} node operator -(const node &a,const node &b) {return node(a.x-b.x,a.y-b.y);} node operator *(const node &a,const node &b) {return node(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);} void init(int n) { for (int i=0;i<n;i++) { o[i]=node(cos(2.0*i*pi/n),sin(2.0*i*pi/n)); _o[i]=node(cos(2.0*i*pi/n),-sin(2.0*i*pi/n)); } } void FFT(int n,node *a,node *w) { int i,j=0,k; for (i=0;i<n;i++) { if (i>j) swap(a[i],a[j]); for (int l=n>>1;(j^=l)<l;l>>=1); } for (i=2;i<=n;i<<=1) { int m=i>>1; for (j=0;j<n;j+=i) for (k=0;k<m;k++) { node z=a[j+m+k]*w[n/i*k]; a[j+m+k]=a[j+k]-z; a[j+k]=a[j+k]+z; } } } char A[300010],B[300010]; int s1 ,s2 ; int get(char x) { if (x>='a'&&x<='z') return x-'a'+1; else return 0; } int main() { scanf("%d%d",&m,&n); scanf("%s",A); scanf("%s",B); for (int i=0;i<m;i++) s1[m-1-i]=get(A[i]); for (int i=0;i<n;i++) s2[i]=get(B[i]); fn=1; while (fn<=n+m) fn<<=1; init(fn); for (int i=0;i<fn;i++) a[i]=node(s1[i]*s1[i]*s1[i],0); for (int i=0;i<fn;i++) b[i]=node(s2[i],0); FFT(fn,a,o); FFT(fn,b,o); for (int i=0;i<fn;i++) c[i]=a[i]*b[i]; for (int i=0;i<fn;i++) a[i]=node(2.0*s1[i]*s1[i],0); for (int i=0;i<fn;i++) b[i]=node(s2[i]*s2[i],0); FFT(fn,a,o); FFT(fn,b,o); for (int i=0;i<fn;i++) c[i]=c[i]-a[i]*b[i]; for (int i=0;i<fn;i++) a[i]=node(s1[i],0); for (int i=0;i<fn;i++) b[i]=node(s2[i]*s2[i]*s2[i],0); FFT(fn,a,o); FFT(fn,b,o); for (int i=0;i<fn;i++) c[i]=c[i]+a[i]*b[i]; FFT(fn,c,_o); int cnt=0; for (int i=0;i<=n-m;i++) if (c[i+m-1].x/fn<0.5) ans[++cnt]=i+1; printf("%d\n",cnt); for (int i=1;i<=cnt;i++) printf("%d ",ans[i]); return 0; }
相关文章推荐
- [BZOJ4259]残缺的字符串(FFT)
- [BZOJ4259][FFT]残缺的字符串
- BZOJ 4259 残缺的字符串 ——FFT
- [BZOJ4259] 残缺的字符串 (FFT)
- [BZOJ4259] 残缺的字符串 FFT
- BZOJ4259 残缺的字符串 【fft】
- 【BZOJ4259】残缺的字符串-FFT
- BZOJ 4259: 残缺的字符串 [FFT]
- bzoj4259 残缺的字符串(字符串+fft)
- BZOJ 4259 残缺的字符串(FFT)
- 【BZOJ】4259: 残缺的字符串 FFT
- 【BZOJ4259】残缺的字符串 FFT
- [bzoj4259][bzoj4503] 残缺的字符串 [FFT]
- bzoj 4259: 残缺的字符串 (FFT)
- BZOJ4259 残缺的字符串(FFT)
- BZOJ 4259: 残缺的字符串(构造+FFT)
- bzoj4259: 残缺的字符串
- BZOJ4259 : 残缺的字符串
- bzoj 4259 残缺的字符串
- bzoj 4259: 残缺的字符串