【题解】2021HDU多校第三场 HDU6975 Forgiving matching
2021HDU多校第三场 HDU6975 Forgiving matching
题意
给一个串\(A\),和串\(B\)(均仅含字符'1'-'9'或通配符'*'),其中通配符可以匹配任意字符,分别求出最多容许\(k\)个字符失配的情况下(\(0\le k\le m\)),有多少个\(A\)的子串和\(B\)匹配。
\(1\le |B|\le |A|\le 2\times 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]<<"\n";
}
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;cin>>t;
while(t--)
f1();
return 0;
}