TJOI 2017DNA 【bzoj4892&&luogu3763】

(http://www.elijahqi.win/2017/07/21/tjoi-2017dna-%E3%80%90bzoj4892luogu3763%E3%80%91/)
题目描述

加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状。现在研究人员想知道这个基因在DNA链S0上的位置。所以你需要统计在一个表现出吃藕性状的人的DNA序列S0上,有多少个连续子串可能是该基因,即有多少个S0的连续子串修改小于等于三个字母能够变成S。

输入输出格式

输入格式:

第一行有一个数T,表示有几组数据 每组数据第一行一个长度不超过10^5的碱基序列S0

每组数据第二行一个长度不超过10^5的吃藕基因序列S

输出格式:

共T行,第i行表示第i组数据中,在S0中有多少个与S等长的连续子串可能是表现吃藕性状的碱基序列

输入输出样例

输入样例#1:

1
ATCGCCCTA
CTTCA
输出样例#1:

2
说明

对于20%的数据,S0,S的长度不超过10^4

对于20%的数据,S0,S的长度不超过10^5,0< T<=10

天津今年的省选题很有质量啊,测试数据也非常好,不像往年写写暴力就能过不少,于是 今年就差点爆0了 ORZ

一道后缀数组加暴力的题 期望复杂度在nlogn+n吧 可能会算错,不太会算(。・∀・)ノ希望指正

我自己在bzoj上似乎被卡常了,因为用的是何琦的sa??黑人问号,省选的时候lzh学长当场写过,看了看他的程序似乎是用了坊间传说的da算法。我们省选是开o2优化的,于是在洛谷上 大牛模式(有o2优化) 我就交过了

程序挺长的 大概因为我一开始总是过不去加的调试太多 前面rmq+sa求lcp可以看下我之前的文章

SA:http://www.elijahqi.win/2017/07/12/poj-2774/

RMQ:http://www.elijahqi.win/2017/07/13/%e3%80%90luogu2880%e3%80%91usaco07jan%e5%b9%b3%e8%a1%a1%e7%9a%84%e9%98%b5%e5%ae%b9balanced-lineup/

SA+RMQ求lcp:

http://www.elijahqi.win/2017/07/15/ural1297/

大概说一下暴力部分如何去求,由于说我们可以nlogn求lcp然后o1的时间查询

我们枚举原串的每一个字符作为起点,如果相同就用lcp求,不相同就给记录不相同的计数器++

保证不相同的计数器不超过3 如果在3以内我们就统计答案

#include<cstdio>
#include<cstring>
#include<cmath>
#define N 220000
int T,n,n1,m,count[N],tmp[N],rank[N<<1],rank1[N],sa[N],height[N],k,fmin1[N][18];
char a[N];
inline void swap(int &a,int &b){
    int t=a;a=b;b=t;
}
inline int min(int x,int y){
    return x<y?x:y;
}
inline int lcp(int l,int r){
    int a=rank[l],b=rank[r];
    if (a>b) swap(a,b);a++;
    int k=0;k=(int)log2(b-a+1);//while ((1<<(k+1))<=(b-a+1))++k;
    return min(fmin1[a][k],fmin1[b-(1<<k)+1][k]);
}
int main(){
    //freopen("3763.in","r",stdin);
//  freopen("3763.out","w",stdout);
    scanf("%d",&T);
    while (T--){
    /*  char ch=getchar();n1=0;
        while (ch<'A'||ch>'Z') ch=getchar();
        while (ch<='Z'&&ch>='A') {
            a[++n1]=ch;ch=getchar();    
        }*/
        scanf("%s",a+1);n1=strlen(a+1);
        a[++n1]='#';
        scanf("%s",a+n1+1);
        n=strlen(a+1); 
    /*  n=n1;
        while (ch<'A'||ch>'Z') ch=getchar();
        while (ch<='Z'&&ch>='A') {
            a[++n]=ch;ch=getchar(); 
        }
        n=strlen(a+1);*/
        //n1=n-n1;
        m=5;
        //printf("%d ",n1);
    //  for (int i=1;i<=n;++i) printf("%c",a[i]);
        for (int i=1;i<=255;++i) count[i]=0;
        memset(rank,0,sizeof(rank));
        for (int i=1;i<=n;++i) count[a[i]]=1;
        for (int i=1;i<=255;++i) count[i]+=count[i-1];
        for (int i=1;i<=n;++i) rank[i]=count[a[i]];

        k=0;
        for (int p=1;k!=n;p<<=1,m=k){
            for (int i=1;i<=m;++i) count[i]=0;
            for (int i=1;i<=n;++i) count[rank[i+p]]++;
            for (int i=1;i<=m;++i) count[i]+=count[i-1];
            for (int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
            for (int i=1;i<=m;++i) count[i]=0;
            for (int i=1;i<=n;++i) count[rank[i]]++;
            for (int i=1;i<=m;++i) count[i]+=count[i-1];
            for (int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
            memcpy(rank1,rank,sizeof(rank)>>1);
            rank[sa[1]]=k=1;
            for (int i=2;i<=n;++i){
                if (rank1[sa[i-1]]!=rank1[sa[i]]||rank1[sa[i-1]+p]!=rank1[sa[i]+p])++k;
                rank[sa[i]]=k;
            }
        }
    /*  for (int i=1;i<=n;++i){
            for (int j=sa[i];j<=n;++j) printf("%c",a[j]);printf("\n");
        }*/
        k=0;
        for (int i=1;i<=n;++i){
            if (rank[i]==0){
                height[1]=0;continue;
            }
            k=k==0?0:k-1;
            while (a[i+k]==a[sa[rank[i]-1]+k])++k; 
            height[rank[i]]=k;
        }
        //for (int i=1;i<=n;++i) printf("%d ",height[i]);
    //  memset(fmin1,0x7f,sizeof(fmin1));
        for (int i=1;i<=n;++i) fmin1[i][0]=height[i];
        for (int j=1;(1<<j)<=n;++j){
            for (int i=1;i+(1<<j)-1<=n;++i){
                fmin1[i][j]=min(fmin1[i][j-1],fmin1[i+(1<<(j-1))][j-1]);
                //printf("%d ",fmin[i][j]);
            }
        //  printf("\n");
        }
    /*  int d=n1;int ans=0;
        for (int i=1;i<=n;++i){
            if (height[i]>=n1-3){
                if ((sa[i]/d)^(sa[i-1]/d)) ans++;
            }
        }*/
        int na=n1,nb=n-n1,ans=0;
        for (int i=1;i<=na-nb;i++){
            int tot=0;
            for (int j=1;j<=nb&&tot<=3;)
                if (a[i+j-1]!=a[na+j]) {
                    tot++,j++;if (tot>3) break;
                }
                else j+=lcp(i+j-1,na+j);
            if (tot<=3) ans++;
        //  if (tot>3) continue;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值