BZOJ#4503. 两个串
4503: 两个串
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1128 Solved: 457
Description
兔子们在玩两个串的游戏。给定两个字符串S和T,兔子们想知道T在S中出现了几次,
分别在哪些位置出现。注意T中可能有“?”字符,这个字符可以匹配任何字符。
Input
两行两个字符串,分别代表S和T
Output
第一行一个正整数k,表示T在S中出现了几次
接下来k行正整数,分别代表T每次在S中出现的开始位置。按照从小到大的顺序输出,S下标从0开始。
Sample Input
bbabaababaaaaabaaaaaaaabaaabbbabaaabbabaabbbbabbbbbbabbaabbbababababbbbbbaaabaaabbbbbaabbbaabbbbabab
a?aba?abba
a?aba?abba
Sample Output
0
HINT
S 长度不超过 10^5, T 长度不会超过 S。 S 中只包含小写字母, T中只包含小写字母和“?”
Problem:
给定一个文本串(只包含a,b)和一个匹配串,匹配串中会有通用符(与任意字符都匹配),问文本串中有多少和匹配串相同的子串
Solution:
先看没有通用符怎么判断匹配
怎么判断一个字符匹配?
它们相等就匹配
怎么判断一个字符串匹配?
它们中每个对应的字符匹配就匹配
那么在这里可得一个式子:
Σ(a[i]-b[i])=0
可是会出现前面与后面刚好相加为0的情况
那么再改进一下:
Σ[(a[i]-b[i])^2]=0
如果有通用符怎么办?
Σ[b[i]*(a[i]-b[i])^2]=0 (b为匹配串,赋通用符为0,那么如果它为通用符,或者和a相等就匹配)
再化解一下式子:
ans=Σb[i]*(a[i]*a[i]-2*a[i]*b[i]+b[i]*b[i]
=Σa[i]*a[i]*b[i]-Σ2*a[i]*b[i]*b[i]+Σb[i]*b[i]*b[i]
式子解决了,我们怎么求?
看这个形式,如果我们把b反一下
我们就可以用卷积求得
可能没想清楚的同学会问,怎么保证卷积算出来的刚好就是一段连续的len2长度的子串
那么可以想一下,举个例子:
a=0 1 2 3 4 5 /b=0 1 2
当正在找以5为结尾的子串时
我们的卷积算的是Σa[i]+b[j](i+j==5)
我们b的j只会有0,1,2
那么我们的a只会选到相加等于5的编号的值,也就是5,4,3
可以看到我们刚好就是选到这个子串
ok!
附上代码:
#include<bits/stdc++.h> using namespace std; const double pi=acos(-1); const int N=880010; char s1[N],s2[N]; int n; struct Complex { double x,i; Complex(){} Complex(double a,double b) {x=a;i=b;} }A[N],B[N],C[N]; Complex operator + (Complex a,Complex b) {return Complex(a.x+b.x,a.i+b.i);} Complex operator - (Complex a,Complex b) {return Complex(a.x-b.x,a.i-b.i);} Complex operator * (Complex a,Complex b) {return Complex(a.x*b.x-a.i*b.i,a.x*b.i+a.i*b.x);} int rev[N]; void FFT(Complex *a,int t) { for(int i=0;i<n;i++) if(rev[i]>i) swap(a[i],a[rev[i]]); for(int i=1;i<n;i<<=1) { Complex wn(cos(2*pi/(i<<1)),t*sin(2*pi/(i<<1))); for(int j=0;j<n;j+=(i<<1)) { Complex w(1,0),t0,t1; for(int k=0;k<i;k++) { t0=a[j+k];t1=w*a[i+j+k]; a[j+k]=t0+t1; a[i+j+k]=t0-t1; w=w*wn; } } } } int ans[N],cnt; int a[N],b[N]; int main() { freopen("a.in","r",stdin); scanf("%s%s",s1,s2); int len1=strlen(s1),len2=strlen(s2); n=1; int len=0; while(n<len1*2) n<<=1,len++; rev[0]=0;for(int i=1;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1)); for(int i=0;i<len1;i++) a[i]=s1[i]-'a'+1; for(int i=0;i<len2;i++) b[len2-i-1]=s2[i]=='?'?0:s2[i]-'a'+1; for(int i=0;i<n;i++) A[i]=Complex(b[i]*b[i]*b[i],0),B[i]=Complex(1,0); FFT(A,1);FFT(B,1); for(int i=0;i<n;i++) C[i]=A[i]*B[i]; for(int i=0;i<n;i++) A[i]=Complex(2*a[i],0),B[i]=Complex(b[i]*b[i],0); FFT(A,1);FFT(B,1); for(int i=0;i<n;i++) C[i]=C[i]-A[i]*B[i]; for(int i=0;i<n;i++) A[i]=Complex(a[i]*a[i],0),B[i]=Complex(b[i],0); FFT(A,1);FFT(B,1); for(int i=0;i<n;i++) C[i]=C[i]+A[i]*B[i]; FFT(C,-1); for(int i=0;i<n;i++) C[i].x=(int)(C[i].x/n+0.5); for(int i=len2-1;i<len1;i++) if(C[i].x==0) ans[++cnt]=i-len2+1; printf("%d\n",cnt); for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]); return 0; }