阿里“天池”竞赛平台近日推出了一个新的挑战任务:对于给定的一串 DNA 碱基序列 t,判断它在另一个根据规则生成的 DNA 碱基序列 s中出现了多少次。
首先,定义一个序列 w:
wi={b,(wi−1+a)modn,i=0i>0
接下来,定义长度为 n 的 DNA 碱基序列 s(下标从 0 开始):
si=⎩⎪⎪⎪⎨⎪⎪⎪⎧A,T,G,C,(L≤wi≤R)∧(wi mod 2=0)(L≤wi≤R)∧(wi mod 2=1)((wi<L)∨(wi>R))∧(wi mod 2=0)((wi<L)∨(wi>R))∧(wi mod 2=1)
其中 ∧ 表示“且”关系,∨ 表示“或”关系,a mod b 表示 a 除以 b 的余数。
现给定另一个 DNA 碱基序列 t,以及生成 s 的参数 n,a,b,L,R,求 t 在 s 中出现了多少次。
输入格式
数据第一行为 5 个整数,分别代表 n,a,b,L,R。第二行为一个仅包含A
、T
、G
、C
的一个序列 t。
数据保证 0<a<n, 0≤b<n, 0≤L≤R<n, ∣t∣≤106,a,n 互质。
对于简单版本,1≤n≤106;
对于中等版本,1≤n≤109,a=1;
对于困难版本,1≤n≤109。
输出格式
输出一个整数,为 t 在 s 中出现的次数。
样例说明
对于第一组样例,生成的 s 为TTTCGGAAAGGCC
。
样例输入1
13 2 5 4 9 AGG
样例输出1
1
样例输入2
103 51 0 40 60 ACTG
样例输出2
5
#include"iostream"
#include"cstring"
#define MAXX 1000000+5
using namespace std;
int w[MAXX];
char s[MAXX],t[MAXX];
int total=0;
void makeNext(char *pat,int next[])
{
int len=strlen(pat),k=0,cur=1;
next[0]=0;
for(;cur<len;cur++)
{
while(k>0&&pat[k]!=pat[cur]) k=next[k-1];
if(pat[k]==pat[cur]) k++;
next[cur]=k;
}
}
void kmp(char *str,char *pat,int next[])
{
makeNext(pat,next);
int str_len=strlen(str);
int pat_len=strlen(pat);
int k=0,cur=0;
while(cur<str_len)
{
while(k>0&&pat[k]!=str[cur])k=next[k-1];
if(pat[k]==str[cur]) k++;
if(k==pat_len)
{
total++;
}
cur++;
}
}
int main()
{
int n,a,b,l,r;
int next[MAXX];
cin>>n>>a>>b>>l>>r;
cin>>t;
for(int i=0;i<n;i++)
{
if(i==0)
w[i]=b;
else
w[i]=(w[i-1]+a)%n;
if((w[i]>=l&&w[i]<=r)&&w[i]%2==0)
s[i]='A';
else if((w[i]>=l&&w[i]<=r)&&w[i]%2==1)
s[i]='T';
else if((w[i]<l||w[i]>r)&&w[i]%2==0)
s[i]='G';
else if((w[i]<l||w[i]>r)&&w[i]%2==1)
s[i]='C';
}
kmp(s,t,next);
cout<<total;
}