本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@[TOC]
1003 Forgiving Matching
题意
给两个字符串 问与是的子串进行匹配,在允许出错个字符情况下,最多可以匹配多少个的字串。
思路
这道题很像之前做过的石头剪刀布. 像这种字符匹配的问题,一些可以用FFT。 那么怎么使用FFT呢?
PS:不太懂FFT,但又想知道原理。传送门 FFT详解
做预处理。先明白要做11次FFT('0'~‘9’ or ).
eg: 现在我们只看匹配'0'。每个的字串区间能成功匹配多少个。
那么对于s1的处理就是只要是'0'或者,他们对应的多项式系数为1,不是该字符系数为0
然后次数就是串的下标
题意中通配符可以看成任意字符。
关键在与的处理,这里可谓是经典中的经典
s2字符处理,是 的多项式的系数为1,不是 字符,系数为0。
然后这里的关键是串每一个'0'的次数是 .(这里实际上是将s2翻转啦)
然后以此循环执行对其他字符都这样处理,有十一中不同的字符,所以要11次FFT。
再解释一下吧
现在我对匹配0的情况处理
会得到如下
01010 和 0101
然后将,得到的。次数是3的就是第1个子串与在字符成功匹配到的字符个数。次数是4,就是第2个子串,以此类推。
AC代码如下
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const double PI=acos(-1.0);
const int N=1e6+10;
const ll P=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
struct zw{
double r,i;
zw(double x=0,double y=0){
r=x;
i=y;
}
}a[N],b[N];
zw operator *(zw x,zw y){
return zw(x.r * y.r - x.i * y.i, x.i * y.r + x.r * y.i);
}
zw operator +(zw x,zw y){
return zw(x.r+y.r,x.i+y.i);
}
zw operator -(zw x,zw y){
return zw(x.r-y.r,x.i-y.i);
}
int rev[N];
void FFT(zw *a,int len,int o){
rep(i,1,len-1){
if(i<rev[i]){
swap(a[i],a[rev[i]]);
}
}
for(int h=1;h<len;h<<=1){
zw w1=zw(cos(PI/h),o*sin(PI/h));
for(int j=0;j<len;j+=2*h){
zw w0=zw(1,0);
for(int k=j;k<j+h;k++){
zw x=a[k];
zw y=w0*a[k+h];
a[k]=x+y;
a[k+h]=x-y;
w0=w1*w0;
}
}
}
return ;
}
string s1,s2;
int len1,len2;
int sum[N];
int len;
int l;
int need[N];
void FFT_num(char ch){
// cout<<(ch-'0')<<endl;
// memset(a,0,sizeof(a));
// memset(b,0,sizeof(b));
rep(i,0,len){
//清空数组
a[i]=zw(0,0);
b[i]=zw(0,0);
}
rep(i,0,len1-1){
if(s1[i]==ch or s1[i]=='*' or ch=='*'){
a[i].r=1;
a[i].i=0;
// cout<<1;
}else{
a[i].r=0;
a[i].i=0;
// cout<<0;
}
}
// cout<<endl;
rep(i,0,len2-1){
if(s2[i]==ch){
// b[i].r=1;
b[len2-1-i].r=1;
b[len2-1-i].i=0;
}else{
// b[i].r=0;
b[len2-1-i].r=0;
b[len2-1-i].i=0;
}
}
// rep(i,0,len2-1) cout<<b[i].r;
// cout<<endl;
FFT(a,len,1);
FFT(b,len,1);
rep(i,0,len){
a[i]=a[i]*b[i];
}
FFT(a,len,-1);
rep(i,0,len1+len2-2){
int qs=(int)(a[i].r/len + 0.5);
// cout<<i<<" "<<qs<<endl;
sum[i]+=qs;
}
return ;
}
void solve(){
//能不用就不用memset清空 超时啦...
// memset(need,0,sizeof(need));
// memset(sum,0,sizeof(sum));
cin>>len1>>len2;
cin>>s1>>s2;
len=1;l=0;
while(len<(len1+len2)){
len<<=1;
l++;
}
rep(i,0,len-1){
rev[i]=(rev[i>>1]>>1) | ((i&1)<<(l-1));
//数组清空
need[i]=0;
sum[i]=0;
}
for(int i=0;i<=9;i++){
FFT_num('0'+i);
}
FFT_num('*');
for(int i=len2-1;i<=len1-1;i++){
// cout<<(i-len2+1)<<" "<<sum[i]<<" "<<endl;
need[len2-sum[i]]++;
}
rep(i,0,len2){
// cout<<need[i]<<" ";
if(i!=0){
need[i]+=need[i-1];
}
printf("%d\n",need[i]);
}
return ;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int k;
k=read();
while(k--)
solve();
return 0;
}