All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACGAATTCCG". When studying DNA, it is sometimes useful to identify repeated sequences within the DNA. Write a function to find all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule. For example, Given s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT", Return: ["AAAAACCCCC", "CCCCCAAAAA"].
全部的DNA都是有A,C,G,T这四个字母组成的,好比“ACGAATTCCG”。在研究DNA的时候,从DNA序列中找到重复出现的模式是颇有用的。这个问题要求咱们在一个DNA序列中找到出现超过两次的长度为10的子序列。面试
好比,假设DNA序列为AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT,那么找到的知足条件的子序列为["AAAAACCCCC", "CCCCCAAAAA"]算法
其实,咱们只须要找到全部的长度为10的子序列而且判断这些子序列是否存在重复就能够了。若是不考虑生成子字符串的过程,那么这个算法只须要遍历一次字符串就能够完成了。
代码以下:数组
public List<String> findRepeatedDnaSequences(String s) { //记录不是第一次遍历到的结果 Set<String> result = new HashSet<String>(); //记录第一次遍历到的结果 Set<String> visited = new HashSet<String>(); for(int i = 0 ; i<s.length()-9 ; i++){ //得到以i做为起点的长度为10的字符串 String cur = s.substring(i, i+10); if(!visited.add(cur)){ result.add(cur); } } return new ArrayList<String>(result); }
上一题的思路其实基本没有问题,惟一的缺点是,一个长度为n的字符串可以产生n-9个长度为10的子字符串。这n-9个子字符串所占用的空间将会远远超过n-9个整数所占用的空间。若是之间存储字符串,那么极可能会形成内存溢出。所以咱们须要考虑将字符串转化为整数并存储。微信
其实若是是将26个字母所有转化为整数,并用整数表示任意10个字母所组成的字符串是不可能的。由于26个字母意味着每一个字母至少须要5位才能表示出来。好比00000
表明A, 00001
表明B。而10个字母意味着须要5*10=50位,而一个整形是32位。ide
而本题中,只有四个字母A,C,G和T,只须要两位就能够表示这四个字母,分别是:
A----00----0
C----01----1
G----10----2
T----11----3性能
那么ACGAATTCCG对应的二进制码就是00 01 10 00 00 11 11 01 01 10, 再将这个二进制数转换成对应的十进制数。由于每一个字符串对应的二进制长度为20,小于整数的32,所以是可行的。spa
代码以下:code
public List<String> findRepeatedDnaSequences2(String s){ List<String> result = new ArrayList<String>(); Set<Integer> firstTime = new HashSet<Integer>(); Set<Integer> secondTime = new HashSet<Integer>(); //也可使用hashmap,可是用数组的话能够很好的提高性能 char[] map = new char[26]; map['A'-'A'] = 0;//00 map['C'-'A'] = 1;//01 map['G'-'A'] = 2;//10 map['T'-'A'] = 3;//11 char[] sArray = s.toCharArray(); for(int i = 0 ; i<sArray.length-9 ; i++){ int v = 0; for(int j = i ; j<i+10 ; j++){ v <<= 2; v |= map[sArray[j] - 'A']; } //这里使用了短路原理。也就是在字符串被第一次添加到firstTime的时候,将不会触发secondTime的添加 //而第二次遍历时,会触发secondTime的添加行为并将子字符串添加到结果集中 //超过第二次遍历觉得这secondTime的添加会返回false,说明已经添加过,从而避免了重复添加至结果集 if(!firstTime.add(v) && secondTime.add(v)){ result.add(s.substring(i, i+10)); } } return result; }
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注个人微信公众号!将会不按期的发放福利哦~教程