bzoj4503 两个串
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4503
题意:
兔子们在玩两个串的游戏。给定两个字符串S和T,兔子们想知道T在S中出现了几次,
分别在哪些位置出现。注意T中可能有“?”字符,这个字符可以匹配任何字符。
数据范围
S 长度不超过 10^5, T 长度不会超过 S。 S 中只包含小写字母, T中只包含小写字母和“?”
题解:
最开始想的是每个字符跑一遍,26常数果断T。
其实只需要把
?
当作0,
ti(si−ti)2
就可以看是不是相同。
那么能匹配,就是从这一位往后len(T)位的上式的和为0:
fi=∑j=0|T|−1tj(si+j−tj)2
然后把S串倒过来就可以化为卷积的形式:
fi=∑j=0|T|−1tj(si−j−tj)2
就是
fi=∑j=0|T|−1tj(si−j−tj)2
=∑j=0|T|−1s2i−jtj−2si−jt2j+t3j
分别算出来,最后
fi=0
的位就是匹配上了。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=100005;
const int MXN=262144+1000;
const long double Pi=acos(-1);
struct Virt
{
long double r,i;
Virt(){}
Virt(long double r,long double i):r(r),i(i){}
Virt operator+(const Virt &A){return Virt(r+A.r,i+A.i);}
Virt operator-(const Virt &A){return Virt(r-A.r,i-A.i);}
Virt operator*(const Virt &A){return Virt(r*A.r-i*A.i,r*A.i+i*A.r);}
}omg[MXN],_omg[MXN],a[MXN],b[MXN],c[MXN];
int f[MXN],g[MXN],R[MXN],S,T,len,p;
char s[N],t[N];
void FFT(Virt *x,int opt)
{
for(int i=0;i<len;i++) if(i<R[i]) swap(x[i],x[R[i]]);
Virt *w; if(opt==1) w=omg; else w=_omg;
for(int m=2;m<=len;m<<=1)
{
int l=m>>1;
for(int j=0;j<len;j+=m)
for(int i=0;i<l;i++)
{
Virt y=w[len/m*i]*x[i+j+l];
x[i+j+l]=x[i+j]-y;
x[i+j]=x[i+j]+y;
}
}
if(opt==-1) for(int i=0;i<len;i++) x[i].r=(long double)x[i].r/(long double)len;
}
int main()
{
scanf("%s%s",s,t); S=strlen(s); T=strlen(t);
for(int i=0;i<S;i++) f[S-i-1]=(s[i]-'a')+1;
for(int i=0;i<T;i++) g[i]=(t[i]=='?')?0:t[i]-'a'+1;
for(len=1,p=0;len<2*S;len<<=1,p++);
R[0]=0; for(int i=1;i<len;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(p-1));
for(int i=0;i<len;i++) omg[i]=Virt(cos((long double)2*Pi/(long double)len*i),sin((long double)2*Pi/(long double)len*i)),_omg[i]=Virt(omg[i].r,-omg[i].i);
for(int i=0;i<len;i++) a[i]=Virt(f[i]*f[i],0),b[i]=Virt(g[i],0);
FFT(a,1); FFT(b,1);
for(int i=0;i<len;i++) c[i]=a[i]*b[i];
for(int i=0;i<len;i++) a[i]=Virt(1,0),b[i]=Virt(g[i]*g[i]*g[i],0);
FFT(a,1); FFT(b,1);
for(int i=0;i<len;i++) c[i]=c[i]+a[i]*b[i];
for(int i=0;i<len;i++) a[i]=Virt((long double)2*f[i],0),b[i]=Virt((long double)g[i]*g[i],0);
FFT(a,1); FFT(b,1);
for(int i=0;i<len;i++) c[i]=c[i]-a[i]*b[i];
FFT(c,-1);
int cnt=0; for(int i=T-1;i<S;i++) if(((int)(c[i].r+0.5))==0) cnt++;
printf("%d\n",cnt);
for(int i=S-1;i>=T-1;i--) if(((int)(c[i].r+0.5))==0) printf("%d\n",S-i-1);
return 0;
}