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"].算法一:
基本思路,从坐向右依次比较。
public class Solution { public List<String> findRepeatedDnaSequences(String s) { HashSet<String> set = new HashSet<String>(); //set可以除去重复的序列 for(int i = 0 ; i < s.length() - 20 ; i ++ ){ String tmp = s.substring(i,i+10); if(s.indexOf(tmp,i+10) > -1){//从i+10之后搜索是否存在tmp set.add(tmp); } } List<String> list = new ArrayList<String>(set); return list; } }
显然String类中的indexOf方法没有使用类似KMP这样的匹配方法,而是使用了暴力破解。
该怎么提高效率呢?
算法二:
使用KMP,BM等匹配算法来提高效率
但是,尽管在一次遍历比较中效率提高了,但是我们依然需要进行n-10次遍历,
这种向后看的缺点就是我们看不到未来,因此需要对每一个可能的10-long-sequence都向后匹配一次,查询它是否是重复的,所以KMP和BM并不是很适合这种情况。
算法三:
前两种算法都是向后看,也就是从左向右进行。
接下来我们换种思路,我们采用向前看的思路。
就是我们检查一个10-long-sequence是否在以前出现过,需要消耗一定空间(如果一个字符一个字节,那么需要O(n2));我们是否可以减小消耗空间?
注意到整个序列中只有4种字符A,C,G,T.
而且他们各自的ASCII码是
A: 0100 0001 C: 0100 0011 G: 0100 0111 T: 0101 0100
只有后三位不同,而一个int型是32位,所以10-long-sequence只需要一个4个字节就可以存储了。
而将十个字符组成一个整数,才用的是<<和&,如:cur = (cur << 3) | (s.charAt(index) & 0x07)
当要结合第一到十一个字符时,先提取出后27位,再<<3,然后将第十一个字符加到尾部,如:cur = ((cur & 0x07ffffff) << 3) | (s.charAt(index++) & 0x07);
既然要向前看,那么我们就需要保存遍历前的子序列,故需要一个map。
参考自http://www.cnblogs.com/grandyang/p/4284205.html(这是C++版的)
public class Solution { public List<String> findRepeatedDnaSequences(String s) { Map<Integer,Integer> map = new HashMap<Integer,Integer>(); List<String> list = new ArrayList<String>(); if(s.length() < 10 ){ return list; } int index = 0; int cur = 0;//用来存储10-long-sequence for( ; index < 9; index ++){//提取出了前九个字符 cur = (cur << 3) | (s.charAt(index) & 0x07); } while(index < s.length()){ cur = ((cur & 0x07ffffff) << 3) | (s.charAt(index++) & 0x07);//提取10-long-sequence的第十个字符; if(map.containsKey(cur)){//前面是否存在cur这个序列 int count = map.get(cur); if(count == 1){//cur在前面仅出现了一次 list.add(s.substring(index - 10,index));//如果存在说明这个序列出现过,则加入list } map.put(cur,++count); }else{//不存在,则添加一个新的键值对 map.put(cur,1); } } return list; } }
这个题要注意到的是只有四个字符,如果有26个字符呢?显然也就不合适来(7*26bit)
所以又有人提出了将四个字符编码,A :00; C:01; G:10; T:11
20bit就可以表示10-long-sequence,看着仿佛减少了存储空间,实际上并没有,还是用int来保存(我还是认为上面的算法,至少不用自己管字符和编码之间的转换)
接下来的思路还是向前看的思路。