1003 Forgiving Matching

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-该下标值 len21.(这里实际上是将s2翻转啦)
然后以此循环执行对其他字符都这样处理,有十一中不同的字符,所以要11次FFT。
再解释一下吧
s 1 : 012 ∗ 4 s1:012*4 s1:0124
s 2 : 0 ∗ 03 s2:0*03 s2:003
现在我对匹配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)=0x0+1x1+0x2+1x3+0x4
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)=0x0+1x1+0x2+1x3
然后将 F = f 1 ∗ f 2 F=f_1*f2 F=f1f2,得到的 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

axtices

谢谢您的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值