题目
生物学家正在对n个物种进行研究。
其中第i个物种的DNA序列为s[i],其中的第j个碱基为s[i][j],碱基一定是A、T、G、C之一。
生物学家想找到这些生物中一部分生物的一些共性,他们现在关注那些至少在m个生物中出现的长度为k的连续碱基序列。准确的说,科学家关心的序列用2m元组(i1,p1,i2,p2…im,pm)表示,
满足:
1<=i1<i2<…<im<=n;
且对于所有q(0<=q<k), s[i1][p1+q]=s[i2][p2+q]=…=s[im][pm+q]。
现在给定所有生物的DNA序列,请告诉科学家有多少的2m元组是需要关注的。如果两个2m元组有任何一个位置不同,则认为是不同的元组。
【输入格式】
输入的第一行包含三个整数n、m、k,两个整数之间用一个空格分隔,意义如题目所述。
接下来n行,每行一个字符串表示一种生物的DNA序列。
DNA序列从1至n编号,每个序列中的碱基从1开始依次编号,不同的生物的DNA序列长度可能不同。
【输出格式】
输出一个整数,表示关注的元组个数。
答案可能很大,你需要输出答案除以1000000007的余数。
【样例输入】
3 2 2
ATC
TCG
ACG
【样例输出】
2
再例如:
【样例输入】
4 3 3
AAA
AAAA
AAA
AAA
【样例输出】
7
【数据规模与约定】
对于20%的数据,k<=5,所有字符串总长L满足L <=100
对于30%的数据,L<=10000
对于60%的数据,L<=30000
对于100%的数据,n<=5,m<=5,1<=k<=L<=100000
保证所有DNA序列不为空且只会包含’A’ ’G’ ’C’ ’T’四种字母
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
题解
暴力+dfs。
首先由于网站上并未给出数据规模,因此我这里特意找了一下规模,发现n<=5,m<=5,这就好说了。
本题的本质思路就是暴力,只不过在其中的一些实现上不得不使用dfs,也就是说dfs只是工具。
整体思路:
我们得存俩东西,一个是每个字符串的每种长度为k的子串的个数(用
A
A
A代指),另一个要存全部字符串不同的长度为
k
k
k的子串有哪些(用
B
B
B代指);
我们遍历
B
B
B,对于枚举到的每个子串
s
u
b
s
t
r
substr
substr,我们都遍历
A
A
A中
n
n
n个字符串中
s
u
b
s
t
r
substr
substr的个数,若其
s
u
b
s
t
r
substr
substr个数大于
0
0
0,则将
s
u
b
s
t
r
substr
substr的个数加入到一个数组(vector)中;若数组中的元素个数大于等于
m
m
m则说明至少可以构成一个我们需要的序列;
假设数组中的元素有
t
t
t个,我们从中选
m
m
m个的方案数就是组合数
C
t
m
C_t^m
Ctm吧,这只是组合数,我们实际要将选出来的这
m
m
m个数进行相乘,最后再把每一种组合的乘积相加,就得到了对于子串
s
u
b
s
t
r
substr
substr能得到的满足要求的序列数了,最终我们将全部子串的序列数相加即为答案,乘加过程中取模即可;
从
t
t
t个数中选
m
m
m个的乘积需要通过dfs求,求法也很简单,对于某个位置的数无非就是选和不选两种情况。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 10;
const ll MOD = 1000000007;
int n, m, k;
ll ans, res;
string s;
vector<int> v; // 代码中讲
map<string, int> cnt[N], vis; // cnt[i][str]表示第i个字符串中长度为k的子串str的个数 vis[str]表示str是否为全部字符串中某个字符串的长度为k的子串
void dfs(int x, int num, ll sum) { // x表示v中的索引,num表示已选个数,sum表示已选数的乘积
if(num == m) {res = (res + sum)%MOD; return ;}
if(x == v.size()) return ;
dfs(x+1, num, sum);
dfs(x+1, num+1, sum*v[x]%MOD);
}
int main()
{
cin>>n>>m>>k;
for(int i = 1;i <= n;i ++) {
cin>>s;
int nn = s.size();
for(int j = 0;j < nn-k+1;j ++) {
string str = s.substr(j, k); // 获取全部长度为k的子串
cnt[i][str] ++; // 第i个字符串的子串str个数+1
vis[str] = 1; // 标记存在子串str
}
}
for(map<string,int>::iterator it = vis.begin();it != vis.end();it ++) { // 枚举子串
v.clear(); // !!!
for(int i = 1;i <= n;i ++) {
string tmp = it->first;
int num = cnt[i][tmp]; // 第i个字符串中子串tmp的个数
if(num) v.push_back(num); // 第i个字符串中子串tmp的个数不为0,则加入数组中
}
if(v.size() >= m) {
res = 0LL; // res用于暂存每种组合的乘积
dfs(0, 0, 1LL);
ans = (ans + res) % MOD;
}
}
cout << ans;
return 0;
}