【纪中集训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;
}

转载于:https://www.cnblogs.com/Paul-Guderian/p/10532904.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值