leetcode187. Repeated DNA Sequences

题目要求

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"]算法

思路一:直接比较String

其实,咱们只须要找到全部的长度为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);
    }

思路二:将String转化为Integer

上一题的思路其实基本没有问题,惟一的缺点是,一个长度为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;
        
    }

clipboard.png
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注个人微信公众号!将会不按期的发放福利哦~教程

相关文章
相关标签/搜索