LeetCode--187. Repeated DNA Sequences

题目链接:https://leetcode.com/problems/repeated-dna-sequences/

要求寻找长度为10的DNA重复子字符串

思路一:这里可以考虑一个HashMap来存储出现的子字符串及其出现次数,出现第二次的则加入最终答案中,而首次出现的就加入Hashmap中,三次及三次以上出现的不加入只是更新出现次数。思路比较朴素,代码如下:

class Solution {
    public List<String> findRepeatedDnaSequences(String s) {
        LinkedList<String> ret=new LinkedList<String>();
        HashMap<String,Integer> hs=new HashMap<String,Integer>();
        for(int i=0;i<s.length()-9;i++)
        {
                int j=i+9;
                String str=s.substring(i,j+1);
                if(hs.containsKey(str))
                {
                    int frequency=hs.get(str);
                    if(frequency==1)
                        ret.add(str);
                    hs.put(str,frequency+1);
                }
                else
                {
                    hs.put(str,1);
                }
        }
        return ret;
    }
}

时间复杂度:O(10m)=O(m)

空间复杂度:O(m)

这个解法效率也很一般。

思路二:总体方法就是来判重。上述思路可以再进一步简化,可以用一个HashSet来存储所有已经出现的子字符串,然后将重复出现的子字符串存到另一个HashSet中,这样就不用管它到底第几次出现了,因为重复出现的子字符串是无法插入集合中的,代码如下:

class Solution {
    public List<String> findRepeatedDnaSequences(String s) {
        HashSet<String> seen = new HashSet<>();
        HashSet<String> reap = new HashSet<>();
        for(int i=0; i<s.length()-9;i++) {
            String temp = s.substring(i,i+10);
            if(!seen.add(temp)) {
                reap.add(temp);
            }
        }
        
        return new ArrayList(reap);
    }
 }

思路三:上面的思路本质都是在哈希函数,而哈希函数可以利用rolling hash的方法来减少计算哈希值的复杂度,这里具体参考这篇算法详解https://blog.csdn.net/To_be_to_thought/article/details/85038546,这里不展示具体代码了。

思路四:因为只有四个字母的情况,我们可以考虑将这四个字母来重现编码,以实现只有四个字母组成的字母表。观察发现:

ASCII码是一个字节表示的字符表,用0-255的十进制数来表示字符
         A的二进制编码:0100 0001
         C的二进制编码:0100 0011
         G的二进制编码:0100 0111
         T的二进制编码:0101 0100
从编码的角度看后三位就可以分别表示这四个字符了,也就是说连续10个字符的信息存储需要30个bit位,而int型是四个字节32个bit,足以存储连续10个字母的编码信息。编码信息的存储是取整型数的二进制表达的后30位置,需要一个掩模0x3fffffff,并且取每个字符的后三位需要一个掩模十进制7(二进制0111)

代码如下:

class Solution {
    
    public List<String> findRepeatedDnaSequences( String s)
    {
        LinkedList<String> ret=new LinkedList<>();
        if(s==null || s.length()<=9)
            return ret;
        int hash=0;

        HashMap<Integer,Integer> map=new HashMap<>();

        int mask=0x3FFFFFFF;
        for(int i=0;i<9;i++)
            hash=(hash<<3) | (s.charAt(i) & 7);
        map.put(hash,1);
        for(int i=9;i<s.length();i++)
        {
            hash= (hash<<3) & mask | ( s.charAt(i) & 7);
            if(map.containsKey(hash))
            {
                int p=map.get(hash);
                if(p==1)
                    ret.add(s.substring(i-9,i+1));
                map.put(hash,++p);
            }
            else
                map.put(hash,1);
        }
        return ret;
    }
}

我后来看了最高效率的解法也是基于思路三的再编码思想来做的,只不过四个字母的字母表只需要4个整数(0-3)来编号(映射),也就是先将这四个字母映射成0,1,2,3,这样原来的30位编码表达变成了20位编码表达,掩模也换成了0xfffff(二进制表达为00000000 00001111 11111111 11111111),代码如下:

class Solution {
    
    public List<String> findRepeatedDnaSequences( String s){
        
        List<String> result = new ArrayList();
        if (s == null || s.length() < 10)
            return result;
        int[] map = new int[26];
        map['A'-'A'] = 0;
        map['C'-'A'] = 1;
        map['G'-'A'] = 2;
        map['T'-'A'] = 3;
        int mask = 0xfffff;
        int hash = 1;
        for( int i= 0; i < 9; i ++ )
        {
            hash = (hash << 2 ) | map[s.charAt(i)-'A'];
        }
        byte[] set = new byte[1<<20];
        for( int i = 9; i < s.length(); i ++ )
        {
            hash = ((hash << 2) & mask ) | map[s.charAt(i)-'A'];
            if( set[hash] == 1 )
            {
                result.add( s.substring( i-9, i + 1));
            }
            if( set[hash] < 2)
            {
                set[hash] ++;
            }
        }
        return result;
    }
}

猜你喜欢

转载自blog.csdn.net/To_be_to_thought/article/details/85204687
今日推荐