1. 程式人生 > >計蒜之道初賽第一場-阿里天池的新任務(簡單)

計蒜之道初賽第一場-阿里天池的新任務(簡單)

阿里“天池”競賽平臺近日推出了一個新的挑戰任務:對於給定的一串 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)(wimod2=0)(LwiR)(wimod2=1)((wi<L)(wi>R))(wimod2=0)((wi<L)(wi>R))(wimod2=1)

其中 \land 表示“且”關係,\lor 表示“或”關係,a\ \mathrm{mod}\ bamodb 表示 aa 除以 bb 的餘數。

現給定另一個 DNA 鹼基序列 tt,以及生成 ss 的引數 n , a , b , L , R

n,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 = 1

1n109,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

題目大意:中文題,不解釋

解題思路:按要求生成文字串,用kmp模板求匹配數。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=1e6+100;
LL ans;
int fail[MAXN];
LL n,a,b,L,R;
char ch[MAXN];

char change(int i)
{
    LL wi=(b+i*a)%n;
    if(wi>=L&&wi<=R)
    {
        if(wi%2==0) return 'A';
        else return 'T';
    }else
    {
        if(wi%2==0) return 'G';
        else return 'C';
    }
}

void getfail(char *p)
{
    int plen=strlen(p);
    fail[0]=0,fail[1]=0;//遞推初值
    for(int i=1;i<plen;i++)
    {
        int j=fail[i];
        while(j&&p[i]!=p[j])
            j=fail[j];
        if(p[i]==p[j])
            fail[i+1]=j+1;
        else
            fail[i+1]=0;
    }
}

void kmp(char *p)
{
    int slen=n;
    int plen=strlen(p);
    int j=0;//當前結點編號
    for(int i=0;i<slen;i++)//文字串當前指標
    {
        while(j&&p[j]!=change(i))//順著失配邊走,直到可以匹配
            j=fail[j];
        if(p[j]==change(i))
            j++;
        if(j==plen)
        {
            ans++;
            //cout<<"find at position "<<i-plen+1<<endl;
            j=fail[j];
        }
    }
}

int main()
{
    while(scanf("%lld%lld%lld%lld%lld",&n,&a,&b,&L,&R)!=EOF)
    {
        scanf("%s",ch);
        ans=0;
        getfail(ch);
//        for(int i=0;i<=strlen(ch);i++)
//        {
//            cout<<fail[i]<<" ";
//        }
//        cout<<endl;
        kmp(ch);
        printf("%lld\n",ans);
    }
    return 0;