文章目录
1003 Forgiving Matching
题意
给两个字符串
s
1
s
2
(
l
e
n
1
>
=
l
e
n
2
)
s1s2(len1>=len2)
s1s2(len1>=len2)
问
s
2
s2
s2与是
s
1
s1
s1的子串进行匹配,在允许出错
k
k
k个字符情况下,最多可以匹配多少个
s
1
s1
s1的字串。
0
<
=
k
<
=
l
e
n
2
0<=k<=len2
0<=k<=len2
思路
这道题很像之前做过的石头剪刀布.
像这种字符匹配的问题,一些可以用FFT。
那么怎么使用FFT呢?
PS:不太懂FFT,但又想知道原理。传送门 FFT详解
做预处理。先明白要做11次FFT(‘0’~‘9’ or
∗
*
∗).
eg: 现在我们只看匹配’0’。每个
s
1
s1
s1的字串区间能成功匹配多少个。
那么对于s1的处理就是只要是’0’或者
∗
*
∗,他们对应的多项式系数为1,不是该字符系数为0
然后次数就是串的下标
题意中通配符
∗
*
∗可以看成任意字符。
关键在与
s
2
s2
s2的处理,这里可谓是经典中的经典
s2字符处理,是
′
0
′
'0'
′0′的多项式的系数为1,不是
′
0
′
'0'
′0′ 字符,系数为0。
然后这里的关键是串
s
2
s2
s2每一个’0’的次数是
l
e
n
2
−
1
−
该
下
标
值
len2-1-该下标值
len2−1−该下标值.(这里实际上是将s2翻转啦)
然后以此循环执行对其他字符都这样处理,有十一中不同的字符,所以要11次FFT。
再解释一下吧
s
1
:
012
∗
4
s1:012*4
s1:012∗4
s
2
:
0
∗
03
s2:0*03
s2:0∗03
现在我对匹配0的情况处理
会得到如下
01010 和 0101
f
1
(
x
)
=
0
∗
x
0
+
1
∗
x
1
+
0
∗
x
2
+
1
∗
x
3
+
0
∗
x
4
f_1(x)=0*x^0+1*x^1+0*x^2+1*x^3+0*x^4
f1(x)=0∗x0+1∗x1+0∗x2+1∗x3+0∗x4
f
2
(
x
)
=
0
∗
x
0
+
1
∗
x
1
+
0
∗
x
2
+
1
∗
x
3
f_2(x)=0*x^0+1*x^1+0*x^2+1*x^3
f2(x)=0∗x0+1∗x1+0∗x2+1∗x3
然后将
F
=
f
1
∗
f
2
F=f_1*f2
F=f1∗f2,得到的
F
F
F。次数是3的就是
s
1
s1
s1第1个子串与
s
2
s2
s2在字符
′
0
′
'0'
′0′成功匹配到的字符个数。次数是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;
}