您的位置:首页 > 其它

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 7

a*b

aebr*ob

Sample Output

2

1 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: