阿里“天池”竞赛平台近日推出了一个新的挑战任务:对于给定的一串 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
思路就是简易字符串匹配的DP思路,如图:
由于2维度数组10^14次方直接越界,所以改成1维,特别注意维度压缩的时候列的遍历需要从后往前,这样才能保证不会覆盖混乱。
如果从前往后,会出现覆盖问题,如图所示:
所以需要改成从后往前的遍历j。改完之后的代码如下图所示,虽然优化了空间复杂度,但时间复杂度仍然是10*14次方,所以只能过11组数据,后面就是TLE。
#include<bits/stdc++.h>
using namespace std;
int t[1000007];
int w[1000007];
int s[1000007];
int dp1[1000007];
int main(void){
int n,a,b,L,R;
cin>>n>>a>>b>>L>>R;
string ss;
int len=0;
cin>>ss;
for(int i=0;i<ss.length();i++){
if(ss[i]=='A'){
t[len++]=1;
}
else if(ss[i]=='T'){
t[len++]=2;
}
else if(ss[i]=='G'){
t[len++]=3;
}
else if(ss[i]=='C'){
t[len++]=4;
}
}
/*
for(int i=0;i<len-1;i++){
cout<<t[i]<<" ";
}cout<<endl;
*/
for(int i=0;i<n;i++){
if(i==0){
w[i]=b;
}
else{
w[i]=(w[i-1]+a)%n;
}
}
for(int i=0;i<n;i++){
if(L<=w[i]&&w[i]<=R&&w[i]%2==0){
s[i]=1;
}
if(L<=w[i]&&w[i]<=R&&w[i]%2==1){
s[i]=2;
}
if(((w[i]<L)||(w[i]>R))&& w[i]%2==0){
s[i]=3;
}
if(((w[i]<L)||(w[i]>R))&& w[i]%2==1){
s[i]=4;
}
//cout<<s[i]<<" ";
}//cout<<endl;
for(int i=0;i<ss.length();i++){
for(int j=n-1;j>=0;j--){
if(t[i]==s[j]){
if(i==0){
dp1[j]=1;
}
else{
if(dp1[j-1]==i){
dp1[j]=max(dp1[j-1]+1,dp1[j]);
}
else{
dp1[j]=max(dp1[j],0);
}
}
}
}
}
int cnt1=0;
for(int i=0;i<n;i++){
if(dp1[i]==ss.length()){
cnt1++;
}
}
cout<<cnt1<<endl;
return 0;
}
另外附上POW同学好心提供的造样例程序= =
#include <cstdio>
#include <cstdlib>
using namespace std;
int main ()
{
freopen ("in.txt","w",stdout);
// srand (666072);
printf ("1000000 ");
int a=rand (),b=rand (),L=rand (),R=rand (),Lent=2;
printf ("%d %d %d %d\n",a,b,L,R);
for (int i=1;i<=Lent;++i)
{
int tmp=rand ()%4+1;
switch (tmp)
{
case 1:printf ("A");break;
case 2:printf ("T");break;
case 3:printf ("G");break;
case 4:printf ("C");break;
}
}
puts ("");
return 0;
}