2017计蒜之道 初赛第一场 阿里天池的新任务(简单)


阿里“天池”竞赛平台近日推出了一个新的挑战任务:对于给定的一串 DNA 碱基序列 tt,判断它在另一个根据规则生成的 DNA 碱基序列 ss中出现了多少次。

首先,定义一个序列 ww

\displaystyle w_{i} = \begin{cases}b, & i = 0\\(w_{i-1} + a) \mod n, & i > 0\end{cases}wi={b,(wi1+a)modn,i=0i>0

接下来,定义长度为 nn 的 DNA 碱基序列 ss(下标从 00 开始):

\displaystyle s_{i} = \begin{cases}A , & (L \le w_{i} \le R) \land (w_{i}\ \mathrm{mod}\ 2 = 0)\\T , & (L \le w_{i} \le R) \land (w_{i}\ \mathrm{mod}\ 2 = 1)\\G , & ((w_{i} < L) \lor (w_{i} > R)) \land (w_{i}\ \mathrm{mod}\ 2 = 0)\\C , & ((w_{i} < L) \lor (w_{i} > R)) \land (w_{i}\ \mathrm{mod}\ 2 = 1)\end{cases}si=A,T,G,C,(LwiR)(wi mod 2=0)(LwiR)(wi mod 2=1)((wi<L)(wi>R))(wi mod 2=0)((wi<L)(wi>R))(wi mod 2=1)

其中 \land 表示“且”关系,\lor 表示“或”关系,a\ \mathrm{mod}\ ba mod b 表示 aa 除以 bb 的余数。

现给定另一个 DNA 碱基序列 tt,以及生成 ss 的参数 n , a , b , L , Rn,a,b,L,R,求 tt 在 ss 中出现了多少次。

输入格式

数据第一行为 55 个整数,分别代表 n , a , b , L , Rn,a,b,L,R。第二行为一个仅包含ATGC的一个序列 tt

数据保证 0 < a < n,0<a<n, 0 \le b < n,0b<n, 0 \le L \le R < n,0LR<n, |t| \le 10^{6}t106a,na,n 互质。

对于简单版本,1 \leq n \leq 10^{6}1n106

对于中等版本,1 \leq n \leq 10^{9}, a = 11n109,a=1

对于困难版本,1 \leq n \leq 10^{9}1n109

输出格式

输出一个整数,为 tt 在 ss 中出现的次数。

样例说明

对于第一组样例,生成的 ss 为TTTCGGAAAGGCC

样例输入1
13 2 5 4 9
AGG
样例输出1
1
样例输入2
103 51 0 40 60
ACTG
样例输出2
5

这道是B题,题目有点绕,仔细分析一下还是挺好懂的,最简单的想法就是根据参数生成S字符串,然后求解的问题就是计算T字符串在S中出现多少次?

我试了这么几个做法:

1.读数据用string,统计用string::find暴力怼,大概只过了不到一半数据,其他超时

2.读数据换成char*,统计用strstr暴力怼,多过了几组数据,最后还有几组超时没过。

3.在2的基础上改进,用KMP算法做,AC

C题和D题题目和这个几乎完全一样,但是数据范围很大,只通过了几个人。。,等我想好再写。


#include <iostream>
#include <string.h>

#define MAX 1000000 + 10

char T[MAX];
char S[MAX];
int next[MAX];

int slen;
int tlen;

void getNext() {
    int j, k;
    j = 0;
    k = -1;
    next[0] = -1;
    while( j < tlen )
        if( k == -1 || T[j] == T[k] )
            next[++j] = ++k;
        else
            k = next[k];

}

int KMP_Count() {
    int ans = 0;
    int i, j = 0;

    if( slen == 1 && tlen == 1 ) {
        if( S[0] == T[0] )
            return 1;
        else
            return 0;
    }
    getNext();
    for( i = 0; i < slen; i++) {
        while( j > 0 && S[i] != T[j] )
            j = next[j];
        if( S[i] == T[j] )
            j++;
        if( j == tlen ) {
            ans++;
            j = next[j];
        }
    }
    return ans;
}

int main() {
    int n, a, b, L, R;

    scanf( "%d%d%d%d%d", &n, &a, &b, &L, &R );
    scanf( "%s", T );

    int cur = 0;
    int pre = -1;
    for( int i = 0; i < n; i++ ) {
        if( i == 0 ) {
            cur = b;
        }
        else {
            cur = ( pre + a ) % n;
        }
        if( cur >= L && cur <= R && cur % 2 == 0 ) S[i] = 'A';
        else if( cur >= L && cur <= R && cur % 2 == 1 ) S[i] = 'T';
        else if( ( cur < L || cur > R ) && cur % 2 == 0 ) S[i] = 'G';
        else if( ( cur < L || cur > R ) && cur % 2 == 1 ) S[i] = 'C';
        pre = cur;
    }
    S[n] = '\0';
    slen = strlen( S );
    tlen = strlen( T );

    printf( "%d\n", KMP_Count() );

    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值