第七届 蓝桥杯 决赛 碱基(哈希,不保证对)

本文介绍了如何使用哈希方法解决蓝桥杯比赛中的一道DNA序列匹配问题。通过分析样例,确定匹配规则,并利用HashMap存储不同长度子串的哈希值及其出现次数。遍历所有字符串,计算所有可能的m元组组合,更新答案并处理可能出现的溢出问题。最后给出了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

碱基


生物学家正在对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,否则按无效代码处理。



后缀数组似乎可以直接套,然而我还不太会写,所以这道还是用相对熟练一点的哈希来做~~~(样例过了,实际

无数据供测,不知道正确与否)


首先看到题目得分析其题意,分析出样例输出是怎样得到的。不妨将n个字符串以str1...strn表示,则

第一个样例是str1与str2这两个字符串长度为2的TC匹配,str2与str3这两个字符串长度为2的CG匹配,共2个要关注

第二个样例是str1、str3、str4有1个AAA,str2中有2个AAA。将总的字符串再分为m元组,(1,2,3)中需要关注

的2m元组有1*2*1个,(1,2,4)中有1*2*1个,(1,3,4)中有1*1*1个,(2,3,4)中有1*2*1个,加起来(1*2*1)*3+1*1*1=7(个)


思路就是,对每个DNA序列,从第一个字符开始,取长度为k的子串,一个一个取过去。关于定长字符串哈希的

写法部分,可见我poj1200解题报告,这里不再赘述。对于这道题,我是用HashMap,Key表示子串哈希值,Value

即哈希值均为Key子串个数(这种题一般来说子串不同哈希值亦不同,子串相同哈希值亦相同),然后边扫边更新。至于

HashSet,则是用来存储这些Key值,即不同哈希值。每扫完一个字符串,就将HashSet里存储的值取出来放到数组

里方便之后处理。(有点类似我上一篇的操作,只不过上一篇用的是TreeSet)

对第一个字符串这样操作,后面的几个字符串亦是如此。将所有n个字符串处理完了,接下来,就是组合搜索,先

从n个字符串里选取m个字符串。选完了以后,以第一个字符串为参考标准,假设其某个哈希值key1对应的value为v1

而剩余m-1个字符串里,哈希值均为key1的子串个数分别为v2,v3,...,vm,则又可得到v1*v2*...*vm个2m元组。这样

不断更新ans即可。这道题还有一个,就是数字较大可能溢出,取模的同时还要用long类型存值


代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.StringTokenizer;

class Reader {
	static BufferedReader reader;
	static StringTokenizer tokenizer;

	static void init(InputStream input) {
		reader = new BufferedReader(new InputStreamReader(input));
		tokenizer = new StringTokenizer("");
	}

	static String next() throws IOException {
		while (!tokenizer.hasMoreTokens()) {
			tokenizer = new StringTokenizer(reader.readLine());
		}
		return tokenizer.nextToken();
	}

	static int nextInt() throws IOException {
		return Integer.parseInt(next());
	}
}

public class MatchP5 {

	/**
	 * @param args
	 */
	static long monum = Integer.MAX_VALUE;
	static long mulnum = 100000007;
	static long ansmo = 1000000007;
	static long val, mval, ans, cnt;
	static int n, m, k, len, nums, v;
	static char ch[][];
	static long chval[];
	static String str;
	static int arr[], hashnum[], size[];
	static int hval[][];
	static HashMap<Integer, Integer> hashMap[];
	static HashSet<Integer> hashSet[];
    static boolean flag;
	private static void dfs(int num, int st) {
		if (num == m + 1) {
			for (int i = 1; i <= size[arr[1]]; i++) {
				v = hval[arr[1]][i];
				cnt = (long) hashMap[arr[1]].get(v) % ansmo;
				flag=true;
				for (int j = 2; j <= m; j++)
					if (hashMap[arr[j]].containsKey(v))
						cnt = cnt * (long) hashMap[arr[j]].get(v) % ansmo;
					else {
						flag=false;
						break;
					}
				if (flag) ans = ans + cnt;

			}
			return;
		}
		for (int i = st; i <= n - m + num; i++) {
			arr[num] = i;
			dfs(num + 1, i + 1);
		}

	}

	private static void dealStr(int num) {
		val = 0;
		len = ch[num].length;
		if (len < k)
			return;
		mval = 1;
		for (int i = 1; i <= k; i++) {
			val = (val * mulnum + chval[ch[num][i - 1]]) % monum;
			mval = (mval * mulnum) % monum;
		}
		hashMap[num].put((int) val, 1);
		hashSet[num].add((int) val);
		for (int i = k + 1; i <= len; i++) {
			val = (val * mulnum + chval[ch[num][i - 1]]) % monum;
			val = ((val - chval[ch[num][i - k - 1]] * mval) % monum + monum)
					% monum;
			if (!hashMap[num].containsKey((int) val)) {
				hashMap[num].put((int) val, 1);
				hashSet[num].add((int) val);
			} else {
				nums = hashMap[num].get((int) val);
				hashMap[num].put((int) val, nums + 1);
			}
		}
		size[num] = hashSet[num].size();
		hval[num] = new int[size[num] + 1];
		Iterator<Integer> iter = hashSet[num].iterator();
		int cnt = 0;
		while (iter.hasNext()) {
			cnt++;
			hval[num][cnt] = iter.next();
		}
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		Reader.init(System.in);
		n = Reader.nextInt();
		m = Reader.nextInt();
		k = Reader.nextInt();
		ch = new char[n + 1][];
		for (int i = 1; i <= n; i++) {
			str = Reader.next();
			ch[i] = str.toCharArray();
		}
		hashMap = new HashMap[n + 1];
		hashSet = new HashSet[n + 1];
		hval = new int[n + 1][];
		size = new int[n + 1];
		chval = new long[200];
		chval['A'] = 0;
		chval['T'] = 1;
		chval['G'] = 2;
		chval['C'] = 3;
		for (int i = 1; i <= n; i++)
			hashMap[i] = new HashMap<Integer, Integer>();
		for (int i = 1; i <= n; i++)
			hashSet[i] = new HashSet<Integer>();
		arr = new int[m + 1];
		for (int i = 1; i <= n; i++)
			dealStr(i);
		ans = 0;
		dfs(1, 1);
		System.out.println(ans);
	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值