2021HDU多校第三场 HDU6975 Forgiving matching
题意
给一个串(A),和串(B)(均仅含字符'1'-'9'或通配符'*'),其中通配符可以匹配任意字符,分别求出最多容许(k)个字符失配的情况下((0le kle m)),有多少个(A)的子串和(B)匹配。
(1le |B|le |A|le 2 imes 10^5,sum|A|le 10^6)
题解
做法类似于Fuzzy search。两个字符匹配当且仅当它们字符相等,或者至少有一个是通配符。由于需要求出(A)中每个长度为(m)的子串有多少个位置与(B)串匹配,我们可以对每个字符分开处理.对于数字字符(c),我们将(A,B)中为(c)的位置置为1,其他位置置为0,翻转(B)串后做(FFT),则(a_i)即为(A)中以(i)结尾的长度为(m)的子串有多少个位置的(c)字符和(B)串对应位置的(c)字符匹配。对于通配符,通过通配符匹配的位置数即为(A的长度为m的子串中通配符的个数+B中通配符的个数-对应位置都是通配符的个数),将上述两种情况相加后即得到(A)中每个长度为(m)的子串有多少个位置与(B)串匹配,即可求答案。
#include <bits/stdc++.h>
typedef double db;
typedef long long ll;
using namespace std;
const double pi=acos(-1.0);
const int N=2300005;
int r[N];
struct cpl{
db x,y;
cpl operator +(const cpl & bb)const{return (cpl){x+bb.x,y+bb.y};}
cpl operator -(const cpl & bb)const{return (cpl){x-bb.x,y-bb.y};}
cpl operator *(const cpl & bb)const{return (cpl){x*bb.x-y*bb.y,x*bb.y+y*bb.x};}
cpl operator /(const db &bb)const{return (cpl){x/bb,y/bb};}
}a[N],b[N];
void fft(cpl *a,int lim,int typ){
for(int i=0;i<=lim;i++)if(i<r[i])swap(a[i],a[r[i]]);
for(int mid=1;mid<lim;mid<<=1){
cpl w1=(cpl){cos(pi/mid),typ*sin(pi/mid)};
for(int R=mid<<1,j=0;j<lim;j+=R){
cpl w=(cpl){1,0};
for(int i=0;i<mid;i++,w=w*w1){
cpl y=a[i+j+mid]*w;
a[i+j+mid]=a[i+j]-y;a[i+j]=a[i+j]+y;
}
}
}
if(typ==-1){
for(int i=0;i<=lim;i++)a[i].x/=lim;
for(int i=0;i<=lim;i++)a[i].y/=lim;
}
}
int lim=0,l=0,n,m;
int f[N],s1[N],s2[N],ans[N];
void f1(){
string A,B;
cin>>n>>m;
cin>>A>>B;
s1[0]=(A[0]=='*');s2[0]=(B[0]=='*');
for(int i=0;i<=n;i++){f[i]=0;ans[i]=0;}
for(int i=1;i<n;i++){s1[i]=s1[i-1]+(A[i]=='*');}
for(int i=1;i<m;i++){s2[i]=s2[i-1]+(B[i]=='*');ans[i]=0;}
lim=1;l=0;while(lim<=(n+m)){lim<<=1;++l;}
for(int i=0;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int o=0;o<10;o++){
char c='0'+o;
for(int i=0;i<=lim;i++){a[i].x=a[i].y=0;b[i].x=b[i].y=0;}
for(int i=0;i<n;i++){
if(A[i]==c){a[i].x=1;}
}
for(int i=0;i<m;i++){
if(B[i]==c){a[m-1-i].y=1;}
}
fft(a,lim,1);
for(int i=0;i<=lim;i++){a[i]=a[i]*a[i];}
fft(a,lim,-1);
for(int i=m-1;i<n;i++){f[i]+=(int)(0.5+a[i].y/2.0);}
}
{ //处理通配符
char c='*';
for(int i=0;i<=lim;i++){a[i].x=a[i].y=0;b[i].x=b[i].y=0;}
for(int i=0;i<n;i++){
if(A[i]==c){a[i].x=1;}
}
for(int i=0;i<m;i++){
if(B[i]==c){a[m-1-i].y=1;}
}
fft(a,lim,1);
for(int i=0;i<=lim;i++){a[i]=a[i]*a[i];}
fft(a,lim,-1);
int ns=s2[m-1];
for(int i=m-1;i<n;i++){
if(i==m-1){
f[i]+=s1[i]+ns-(int)(0.5+a[i].y/2.0);
}
else{
f[i]+=s1[i]-s1[i-m]+ns-(int)(0.5+a[i].y/2.0);
}
}
}
for(int i=m-1;i<n;i++){
ans[m-f[i]]++;
}
for(int i=1;i<=m;i++){
ans[i]+=ans[i-1];
}
for(int i=0;i<=m;i++){
cout<<ans[i]<<"
";
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;cin>>t;
while(t--)
f1();
return 0;
}