题目链接:点击进入
题目
题意
给你两个字符串 S 、T ( 只包含 字符 0 - 9 以及 * ), S 长 n , T 长 m , * 是通配符( 即可以与任何字符匹配 ),问在匹配中最多允许 k( 0 - m )个不同位置的情况下,S 中最多有多少个能与 T 匹配的子串。
思路
对于 S 的所有长度为 m 的子串,维护一个 f 记录这个子串与 T 匹配的位置数,知道了 f 也就是知道了这个子串完全匹配所需要的最小的 k 。
假如没有通配符,对于每个字符 c ,如 果
s
i
s_i
si =
t
j
t_j
tj = c , 那么
f
i
+
m
−
j
f_{i+m-j}
fi+m−j 就应该加 1 ( 以子串右边界单独标记一个子串 ),而这里若是对字符串 T 翻转一下, m - j 就对应 j ,那么 i + m - j 就变成了 i + j 了 ( i + j = k ,
f
k
f_k
fk 就是以 k 为右边界的子串的匹配位置数 ),而这时候我们就可以通过FFT求出字符 c 对应的 f 了( 也就是求子串与T对应位置都是字符c的位置数)。枚举所有的 c ,然后对应系数相加,就是不带通配符的 f 。
对于通配符,S的子串通过使用通配符来匹配的位置数 = S子串的通配符数 + T的通配符数 - 两者对应位置都是通配符的位置数 。 S 子串的通配符数可以通过前缀和解决,T 的通配符数可以直接遍历求和,两者对应位置都是通配符的位置数,这就跟上面的对应位置都为字符 c 是一样的做法,用FFT求解即可 。
求出来的 f 数组,进行 m -
f
i
(
m
−
1
<
=
i
<
=
n
)
f_i(m-1<=i<=n)
fi(m−1<=i<=n)处理,得到最少需要 k 个才能匹配的子串各有多少个,然后对此求前缀和就是所求答案。
( S 子串的通配符数 与 T 的通配符数 通过FFT也可以求 ,就是慢,不过应该也可以过。。。)
代码
#include<iostream>
#include<string>
#include<map>
#include<set>
//#include<unordered_map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<stack>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<fstream>
#define X first
#define Y second
#define best 131
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define lowbit(x) x & -x
#define inf 0x3f3f3f3f
//#define int long long
//#define double long double
//#define rep(i,x,y) for(register int i = x; i <= y;++i)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const int maxn=2e6+10;
const int mod=998244353;
const double eps=1e-7;
const int N=5e3+10;
inline int read()
{
int k = 0, f = 1 ;
char c = getchar() ;
while(!isdigit(c)){if(c == '-') f = -1 ;c = getchar() ;}
while(isdigit(c)) k = (k << 1) + (k << 3) + c - 48 ,c = getchar() ;
return k * f ;
}
int n,m,k,ans[maxn],f[maxn],sums[maxn],sumt;
int lim=1,len,rev[maxn];
struct node
{
double x,y;
node(double xx=0,double yy=0){x=xx,y=yy;}
node operator * (node Q){return node(x*Q.x-y*Q.y,x*Q.y+y*Q.x);}
node operator + (node Q){return node(x+Q.x,y+Q.y);}
node operator - (node Q){return node(x-Q.x,y-Q.y);}
}a[maxn],b[maxn],p[maxn];
inline void fft(node *A,double flag)
{
for(int i=0;i<lim;i++)
if(i<rev[i])
swap(A[i],A[rev[i]]);
for(int i=1;i<lim;i<<=1)
{
node wn(cos(pai/i),flag*sin(pai/i));
for(int j=0;j<lim;j+=(i<<1))
{
node w(1,0);
for(int k=0;k<i;k++,w=w*wn)
{
node nx=A[j+k],ny=w*A[j+i+k];
A[j+k]=nx+ny;
A[j+i+k]=nx-ny ;
}
}
}
}
char s[maxn],t[maxn];
int main()
{
// ios::sync_with_stdio(false);
scanf("%d",&k);
while(k--)
{
scanf("%d%d",&n,&m);
scanf("%s%s",s,t);
reverse(t,t+m);
while(lim<=m+n-2) lim<<=1,len++;
for(int i=0;i<=lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
for(int i=0;i<=lim;i++) p[i]=node(0,0),ans[i]=0,f[i]=0;
for(int c=0;c<=9;c++)//对应位置都是 c 的位置数
{
for(int i=0;i<=lim;i++) a[i]=node((i<n&&s[i]-'0'==c)?1:0,0);
for(int i=0;i<=lim;i++) b[i]=node((i<m&&t[i]-'0'==c)?1:0,0);
fft(a,1);
fft(b,1);
for(int i=0;i<=lim;i++) p[i]=p[i]+a[i]*b[i];
}
// for(int i=0;i<=lim;i++) a[i]=node((i<n&&s[i]=='*')?1:0,0);
// for(int i=0;i<=lim;i++) b[i]=node((i<m)?1:0,0);
// fft(a,1);
// fft(b,1);
// for(int i=0;i<=lim;i++) p[i]=p[i]+a[i]*b[i];
//
// for(int i=0;i<=lim;i++) a[i]=node((i<n)?1:0,0);
// for(int i=0;i<=lim;i++) b[i]=node((i<m&&t[i]=='*')?1:0,0);
// fft(a,1);
// fft(b,1);
// for(int i=0;i<=lim;i++) p[i]=p[i]+a[i]*b[i];
for(int i=0;i<n;i++) sums[i+1]=0;
sumt=0;
for(int i=0;i<n;i++) //S子串的通配符数
{
if(s[i]=='*') sums[i+1]=1;
sums[i+1]+=sums[i];
}
for(int i=0;i<m;i++) //T的通配符数
if(t[i]=='*')
sumt++;
fft(p,-1);
for(int i=0;i<=lim;i++)
f[i]+=(int)(p[i].x/lim+0.5);
for(int i=0;i<=lim;i++) p[i]=node(0,0);//对应位置都是通配符的位置数
for(int i=0;i<=lim;i++) a[i]=node((i<n&&s[i]=='*')?1:0,0);
for(int i=0;i<=lim;i++) b[i]=node((i<m&&t[i]=='*')?1:0,0);
fft(a,1);
fft(b,1);
for(int i=0;i<=lim;i++) p[i]=a[i]*b[i];
fft(p,-1);
for(int i=0;i<=lim;i++)
f[i]-=(int)(p[i].x/lim+0.5);//减去对应位置都是通配符的位置数
for(int i=m-1;i<n;i++)
{
int tmp=f[i]+sums[i+1]-sums[i-m+1]+sumt;
ans[m-tmp]++;
}
for(int i=1;i<=m;i++)
ans[i]+=ans[i-1];
for(int i=0;i<=m;i++)
printf("%d\n",ans[i]);
}
return 0;
}