目录
Forgiving Matching(FFT+字符串匹配)
题意:
给你两个串a,b长度分别为n,m;a中长度为m的子串与b进行匹配,其中∗代表通配符,
即与任何其他字符相同;当小Q可以原谅字串b有0,1,2,……T个位置可以是不匹配的,
分别输出有a中有多少子串是完全匹配的。
思路:
假如没有通配符,对于每个字符 c ,如果s[i]=t[j]=c,那么f[i+m-j]就应该加1 ( 以子串
右边界单独标记一个子串 ),而这里若是对字符串 t 翻转一下,m-j就对应j,那么i+m-j就
变成了i+j了(i+j=k,f[k]就是以 k 为右边界的子串的匹配位置数 ),而这时候我们就可
以通过FFT求出字符 c 对应的 f 了( 也就是求子串与T对应位置都是字符c的位置数)。FFT
字符串匹配,我们可以枚举每个字符,如果当前字符等于我们枚举的字符,我们就让多项式的
系数为1,否则为零。每次两个多项式相乘,每次累加,就是每个子串的匹配数 。
对于通配符,S的子串通过使用通配符来匹配的位置数 = S子串的通配符数 + T的通配符数 -
两者对应位置都是通配符的位置数 。 S 子串的通配符数可以通过前缀和解决,T 的通配符数
可以直接遍历求和,两者对应位置都是通配符的位置数,这就跟上面的对应位置都为字符 c
是一样的做法,用FFT求解即可 。求出来的 f 数组,进行m-f[i](m−1<=i<=n)处理,得到最
少需要 k 个才能匹配的子串各有多少个,然后对此求前缀和就是所求答案。( S 子串的通配
符数 与 T 的通配符数 通过FFT也可以求 ,就是慢,不过应该也可以过。。。)
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long LL;
typedef pair<int,int> PII;
#define re register
inline int read()
{
int ans=0;
char last=' ',ch=getchar();
while(ch<'0'|ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans;
return ans;
}
int buf[105];
inline void write(int i)
{
int p = 0;
if(i == 0) p++;
else while(i)
{
buf[p++] = i % 10;
i /= 10;
}
for(int j = p-1; j >= 0; j--) putchar('0' + buf[j]);
}
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e6+7;
const double PI = acos(-1.0);
struct Complex
{
double x,y;
Complex(double _x = 0.0,double _y = 0.0)
{
x = _x;
y = _y;
}
Complex operator-(const Complex &b)const
{
return Complex(x - b.x,y - b.y);
}
Complex operator+(const Complex &b)const
{
return Complex(x + b.x,y + b.y);
}
Complex operator*(const Complex &b)const
{
return Complex(x*b.x - y*b.y,x*b.y + y*b.x);
}
};
/*
*进行FFT和IFFT前的反置变换
*位置i和i的二进制反转后的位置互换
*len必须为2的幂
*/
void change(Complex y[],int len)
{
int i,j,k;
for(int i = 1,j = len/2; i<len-1; i++)
{
if(i < j) swap(y[i],y[j]);
//交换互为小标反转的元素,i<j保证交换一次
//i做正常的+1,j做反转类型的+1,始终保持i和j是反转的
k = len/2;
while(j >= k)
{
j = j - k;
k = k/2;
}
if(j < k) j+=k;
}
}
/*
*做FFT
*len必须是2^k形式
*on == 1时是DFT,on == -1时是IDFT
*/
void fft(Complex y[],int len,int on)
{
change(y,len);
for(int h = 2; h <= len; h<<=1)
{
Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
for(int j = 0; j < len; j += h)
{
Complex w(1,0);
for(int k = j; k < j + h/2; k++)
{
Complex u = y[k];
Complex t = w*y[k + h/2];
y[k] = u + t;
y[k + h/2] = u - t;
w = w*wn;
}
}
}
if(on ==-1)
{
for(int i = 0; i < len; i++)
{
y[i].x /= len;
}
}
}
const int MAXN = 2e6+10;
Complex a[MAXN],b[MAXN],p[MAXN];
char str1[MAXN/2],str2[MAXN/2];
int sum[MAXN],f[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(sum,0,sizeof(sum));
memset(f,0,sizeof(f));
int n,m;
n=read();
m=read();
scanf("%s%s",str1,str2);
reverse(str2,str2+m);
int len=1;
while(len<=m+n-2) len<<=1;
for(int i=0; i<=len; i++) p[i]=Complex(0,0),f[i]=0;
//枚举每一个字符匹配的位置多少
for(char c=0; c<=9; c++)
{
for(int i=0; i<len; i++)
{
if(i<n&&str1[i]-'0'==c)
{
a[i]=Complex(1,0);
}
else
{
a[i]=Complex(0,0);
}
}
for(int i=0; i<len; i++)
{
if(i<m&&str2[i]-'0'==c)
{
b[i]=Complex(1,0);
}
else
{
b[i]=Complex(0,0);
}
}
fft(a,len,1);
fft(b,len,1);
for(int i=0; i<len; i++) p[i]=p[i]+a[i]*b[i];
}
//
//求当前字串T通用符与S串匹配的位置数
for(int i=0; i<len; i++)
{
if(i<n)
{
a[i]=Complex(1,0);
}
else
{
a[i]=Complex(0,0);
}
}
for(int i=0; i<len; i++)
{
if(i<m&&str2[i]=='*')
{
b[i]=Complex(1,0);
}
else
{
b[i]=Complex(0,0);
}
}
fft(a,len,1);
fft(b,len,1);
for(int i=0; i<len; i++) p[i]=p[i]+a[i]*b[i];
//
//求当前字串S通用符与T串匹配的位置数
for(int i=0; i<len; i++)
{
if(i<n&&str1[i]=='*')
{
a[i]=Complex(1,0);
}
else
{
a[i]=Complex(0,0);
}
}
for(int i=0; i<len; i++)
{
if(i<m)
{
b[i]=Complex(1,0);
}
else
{
b[i]=Complex(0,0);
}
}
fft(a,len,1);
fft(b,len,1);
for(int i=0; i<len; i++) p[i]=p[i]+a[i]*b[i];
//
//S串和T串的都是通用符的匹配位置数
for(int i=0; i<len; i++)
{
if(i<n&&str1[i]=='*')
{
a[i]=Complex(1,0);
}
else
{
a[i]=Complex(0,0);
}
}
for(int i=0; i<len; i++)
{
if(i<m&&str2[i]=='*')
{
b[i]=Complex(1,0);
}
else
{
b[i]=Complex(0,0);
}
}
fft(a,len,1);
fft(b,len,1);
for(int i=0; i<len; i++) p[i]=p[i]-a[i]*b[i];
fft(p,len,-1);
for(int i=0; i<len; i++)
{
double sum1=(p[i].x+0.5);
f[i]=int(sum1);
}
for(int i=m-1; i<n; i++)
{
sum[m-f[i]]++;
}
for(int i=1;i<=m;i++)
{
sum[i]+=sum[i-1];
}
for(int i=0;i<=m;i++)
{
printf("%d\n",sum[i]);
}
}
return 0;
}