codeforces 528D. Fuzzy Search(fft)

本文介绍了如何使用快速傅里叶变换(FFT)解决一个涉及DNA字符串的模糊搜索问题。题目要求找出在一个给定字符串s中,另一个字符串t出现的次数,其中字符匹配有一定的规则。通过将字符拆分并进行特定标记,然后利用FFT进行多项式乘法来统计匹配位置,从而得出答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
f f t fft fft好题。
题意简述:给两个字符串 s , t s,t s,t,问 t t t s s s中出现了几次,字符串只由 A , T , C , G A,T,C,G A,T,C,G构成。
两个字符匹配的定义:
s i − k , s i − k + 1 , . . . , s i + k − 1 , s i + k s_{i-k},s_{i-k+1},...,s_{i+k-1},s_{i+k} sik,sik+1,...,si+k1,si+k中存在至少一个字符使得 s j = t l s_j=t_l sj=tl时,我们称 s i s_i si t l t_l tl匹配。
题目会给出 n , m , k , s , t n,m,k,s,t n,m,k,s,t


思路:
我们把这四种字符拆开分别求解。
设当前处理的是字符 x x x,那么我们将 s , t s,t s,t中分别可以跟字符 x x x匹配的位置标记成为 1 1 1,其余不能匹配的位置标记成 0 0 0
这样一来就可以用经典的套路来统计答案啦!
我们把操作之后的 t t t翻转,然后把 s , t s,t s,t做多项式乘法,算出来如果某一位 h h h的值等于 t t t 1 1 1的个数说明 s h , h + ∣ t ∣ − 1 = t s_{h,h+|t|-1}=t sh,h+t1=t,然后我们做四遍把 四次都匹配的位置数统计出来即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
struct Cp{
	double x,y;
	friend inline Cp operator+(const Cp&a,const Cp&b){return (Cp){a.x+b.x,a.y+b.y};}
	friend inline Cp operator-(const Cp&a,const Cp&b){return (Cp){a.x-b.x,a.y-b.y};}
	friend inline Cp operator*(const Cp&a,const Cp&b){return (Cp){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}
	friend inline Cp operator/(const Cp&a,const double&b){return (Cp){a.x/b,a.y/b};}
	friend inline Cp operator*(const Cp&a,const double&b){return (Cp){a.x*b,a.y*b};}
};
const int N=2e5+5;
vector<Cp>A,B;
int lim,tim,n,m,K,a[N],b[N],ans[N];
char s[N]; 
vector<int>pos;
inline void init(const int&up){
	lim=1,tim=0;
	while(lim<=up)lim<<=1,++tim;
	pos.resize(lim),A.resize(lim),B.resize(lim);
	for(ri i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
const double pi=acos(-1.0);
inline void fft(vector<Cp>&a,const int&type){
	Cp wn,w,a0,a1;
	for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
	for(ri mid=1;mid<lim;mid<<=1){
		wn=(Cp){cos(pi/mid),sin(pi/mid*type)};
		for(ri j=0,len=mid<<1;j<lim;j+=len){
			w=(Cp){1,0};
			for(ri k=0;k<mid;++k,w=w*wn)a0=a[j+k],a1=w*a[j+k+mid],a[j+k]=a0+a1,a[j+k+mid]=a0-a1;
		}
	}
	if(type==-1)for(ri i=0;i<lim;++i)a[i]=a[i]/lim;
}
struct poly{
	vector<Cp>a;
	poly(int k=0,Cp x=(Cp){0,0}){a.resize(k+1),a[k]=x;}
	inline Cp&operator[](const int&k){return a[k];}
	inline const Cp&operator[](const int&k)const{return a[k];}
	inline int deg()const{return a.size()-1;}
	inline poly extend(const int&k){poly ret=*this;return ret.a.resize(k+1),ret;}
	friend inline poly operator*(const poly&a,const poly&b){
		poly ret;
		int n=a.deg(),m=b.deg();
		init(a.deg()+b.deg());
		for(ri i=0;i<=n;++i)A[i]=a[i];
		for(ri i=n+1;i<lim;++i)A[i]=(Cp){0,0};
		for(ri i=0;i<=m;++i)B[i]=b[i];
		for(ri i=m+1;i<lim;++i)B[i]=(Cp){0,0};
		fft(A,1),fft(B,1);
		for(ri i=0;i<lim;++i)A[i]=A[i]*B[i];
		return fft(A,-1),ret.a=A,ret;
	}
};
inline int calc(char x){return x=='A'?1:(x=='T'?2:(x=='C'?3:4));}
inline void solve(int t){
	poly x(n-1),y(m-1);
	int cnt=0;
	for(ri l=1,r=0,i=1;i<=n;++i){
		while(i+K>r&&r<=n){
			++r;
			if(a[r]==t)++cnt;
		}
		if(i-K>l){
			if(a[l]==t)--cnt;
			++l;
		}
		x[i-1].y=0;
		x[i-1].x=cnt>0;
	}
	int tot=0;
	for(ri i=1;i<=m;++i)y[i-1].x=b[i]==t,y[i-1].y=0,tot+=y[i-1].x;
	reverse(y.a.begin(),y.a.end());
	x=x*y;
	for(ri i=0;i+m-1<=n;++i)if((int)(x[i+m-1].x+0.5)==tot)++ans[i+1];
}
int main(){
	freopen("lx.in","r",stdin);
	scanf("%d%d%d",&n,&m,&K);
	scanf("%s",s+1);
	for(ri i=1;i<=n;++i)a[i]=calc(s[i]);
	scanf("%s",s+1);
	for(ri i=1;i<=m;++i)b[i]=calc(s[i]);
	for(ri i=1;i<=4;++i)solve(i);
	int cnt=0;
	for(ri i=1;i<=n;++i)cnt+=ans[i]==4;
	cout<<cnt;
	return 0;
}
### Codeforces Div.2 比赛难度介绍 Codeforces Div.2 比赛主要面向的是具有基础编程技能到中级水平的选手。这类比赛通常吸引了大量来自全球不同背景的参赛者,包括大学生、高中生以及一些专业人士。 #### 参加资格 为了参加 Div.2 比赛,选手的评级应不超过 2099 分[^1]。这意味着该级别的竞赛适合那些已经掌握了一定算法知识并能熟练运用至少一种编程语言的人群参与挑战。 #### 题目设置 每场 Div.2 比赛一般会提供五至七道题目,在某些特殊情况下可能会更多或更少。这些题目按照预计解决难度递增排列: - **简单题(A, B 类型)**: 主要测试基本的数据结构操作和常见算法的应用能力;例如数组处理、字符串匹配等。 - **中等偏难题(C, D 类型)**: 开始涉及较为复杂的逻辑推理能力和特定领域内的高级技巧;比如图论中的最短路径计算或是动态规划入门应用实例。 - **高难度题(E及以上类型)**: 对于这些问题,则更加侧重考察深入理解复杂概念的能力,并能够灵活组合多种方法来解决问题;这往往需要较强的创造力与丰富的实践经验支持。 对于新手来说,建议先专注于理解和练习前几类较容易的问题,随着经验积累和技术提升再逐步尝试更高层次的任务。 ```cpp // 示例代码展示如何判断一个数是否为偶数 #include <iostream> using namespace std; bool is_even(int num){ return num % 2 == 0; } int main(){ int number = 4; // 测试数据 if(is_even(number)){ cout << "The given number is even."; }else{ cout << "The given number is odd."; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值