【纪中集训2019.3.13】碱基配对

题目:

描述

给出长度为\(n\)的字符串\(A\)和长度为\(m\)的字符串\(B\),仅由字符\(Z,P,S,B\)构成;

定义B在A中的\(p\)位置匹配为:对于任意的\(0 \lt j \lt m\) , 在\(A_{p+j - k}到A_{p+j+k}\) 间存在等于\(B_{j}\)的字符;

\(B\)\(A\)中所有的匹配位置;

范围

$1 \le n,m \le 2e5 \ , \ 0 \le k \le n $

题解:

可以预处理出一个字符的有效区间\([i-k,i+k]\)

然后依次考虑每一个字符然后取并集

然后对于每一个字符,\(FFT\) 找出不合法的位置

#include<bits/stdc++.h>
#define ld double 
using namespace std;
const int N=800010;
const ld pi=acos(-1);
int n,m,k,rev[N],len,L,ok[N],cnt[N];
char S[N],T[N];
char gc(){
	static char*p1,*p2,s[1000000];
	if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
	return(p1==p2)?EOF:*p1++;
}
int rd(){
	int x=0;char c=gc();
	while(c<'0'||c>'9')c=gc();
	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
	return x; 
}
int gt(char *s){
	char c=gc(),*p=s;
	while(!isalpha(c))c=gc();
	while(isalpha(c))*p++=c,c=gc();
	return p-s;
}
struct C{
	ld x,y;
	C(ld _x=0,ld _y=0):x(_x),y(_y){};
	C operator +(const C&A)const{return C(x+A.x,y+A.y);}
	C operator -(const C&A)const{return C(x-A.x,y-A.y);}
	C operator *(const C&A)const{return C(x*A.x-y*A.y,x*A.y+y*A.x);}
	C operator /(const ld&A)const{return C(x/A,y/A);}
}A[N],B[N];
void fft(C*a,int f){
	for(int i=1;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int i=1;i<len;i<<=1){
		C wn=C(cos(pi/i),f*sin(pi/i));
		for(int j=0;j<len;j+=i<<1){
			C w=C(1,0);
			for(int k=0;k<i;++k,w=w*wn){
				C x=a[j+k],y=w*a[j+k+i];
				a[j+k]=x+y,a[j+k+i]=x-y;
			}
		}
	}
	if(!~f){for(int i=0;i<len;++i)a[i]=a[i]/len;}
}
void solve(char x){
	for(int i=0;i<=n;++i)cnt[i]=0;
	for(int i=0;i<n;++i)if(S[i]==x)cnt[max(0,i-k)]++,cnt[min(n,i+k+1)]--;
	for(int i=0;i<n;++i)cnt[i]+=cnt[i-1];
	for(int i=0;i<n;++i)A[i]=C(!cnt[i],0);
	for(int i=0;i<m;++i)B[m-1-i]=C(T[i]==x,0);
	for(int i=n;i<len;++i)A[i]=C(0,0);
	for(int i=m;i<len;++i)B[i]=C(0,0);
	fft(A,1);fft(B,1);
	for(int i=0;i<len;++i)A[i]=A[i]*B[i];
	fft(A,-1);
	for(int i=0;i<=n-m;++i){
		ok[i]&=!(int)(A[m-1+i].x+0.1);
	}
}
int main(){
	freopen("base.in","r",stdin);
	freopen("base.out","w",stdout);
	k=rd();n=gt(S);m=gt(T);
	for(L=0,len=1;len<n+m;len<<=1,L++);
	for(int i=1;i<len;++i){rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));}
	if(n<m){puts("0");return 0;}
	for(int i=0;i<=n-m;++i)ok[i]=1;
	solve('Z');
	solve('P');
	solve('S');
	solve('B');
	int ans=0;
	for(int i=0;i<=n-m;++i)if(ok[i])ans++;
	cout<<ans<<endl;
	return 0;
}
posted @ 2019-03-14 19:30  大米饼  阅读(254)  评论(0编辑  收藏  举报