统计DNA序列中重复出现的子串 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"].
Given s = "ACGTACGTACGTACGT",
Return:
["ACGTACGTAC","CGTACGTACG","GTACGTACGT"]

解决:

① 验证DNA中重复的子串,使用hash表,将出现过的结果保存在hashmap中,并记录其出现的次数,根据其出现次数进行相应的处理。时间 O(N) 空间 O(N)

class Solution {//49ms
    public List<String> findRepeatedDnaSequences(String s) {
        List<String> res = new ArrayList<>();
        Map<String,Integer> map = new HashMap<>();
        for (int i = 10;i <= s.length();i ++){//从第10位开始作为结尾,唯一一位,比较一次子串
            String substr = s.substring(i - 10,i);
            if (map.containsKey(substr)){
                if (map.get(substr) == 1){//第一次遇到,加入结果集
                    res.add(substr);
                }
                map.put(substr,2);//标记为已经遇到过一次了
            }else{//如果不存在,则加入表中
                map.put(substr,1);
            }
        }
        return res;
    }
}

② 使用hash表和位操作。

构成输入字符串的字符只有四种,分别是A, C, G, T,首先考虑将其进行二进制编码:

A -> 00
C -> 01
G -> 10
T -> 11

在编码的情况下,每10位字符串的组合即为一个数字,且10位的字符串有20位;一般来说int有4个字节,32位,即可以用于对应一个10位的字符串。例如:

ACGTACGTAC -> 00011011000110110001

AAAAAAAAAA -> 00000000000000000000

20位的二进制数,至多有2^20种组合,因此hash table的大小为2^20,即1024 * 1024,将hash table设计为bool hashTable[1024 * 1024];

遍历字符串: 每次向右移动1位字符,相当于字符串对应的int值左移2位,再将其最低2位置为新的字符的编码值,最后将高2位置0。时间 O(N) 空间 O(N)。

class Solution { //76ms
    public List<String> findRepeatedDnaSequences(String s) {
        List<String> res = new ArrayList<>();
        HashMap<Integer,Integer> map = new HashMap<>();
        for (int i = 10;i <=s.length();i ++){
            String sub = s.substring(i - 10,i);
            int code = encode(sub);
            if (map.containsKey(code)){
                if (map.get(code) == 1){
                    res.add(sub);
                }
                map.put(code,2);
            }else{
                map.put(code,1);
            }
        }
        return res;
    }
    public int encode(String str){
        int code = 0;
        for (int i = 0;i < str.length();i ++){
            char c = str.charAt(i);
            code <<= 2;//每两位表示一个字符
            switch (c){
                case 'A': code += 0; break;
                case 'C': code += 1; break;
                case 'T': code += 2; break;
                case 'G': code += 3; break;
            }
        }
        return code;
    }
}

③ 使用位操作进行编码,使用hash table保存已经编码的结果。

class Solution{//48ms
    public List<String> findRepeated(String s){
        List<String> res = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return res;
        } 
        Set<Integer> ones = new HashSet<>();
        Set<Integer> twos = new HashSet<>();
        //对字母进行编码
        int[] map = new int[256];
        map['A'] = 0;
        map['C'] = 1;
        map['G'] = 2;
        map['T'] = 3;
        for (int i = 0;i < s.length() - 9 ;i ++ ) {
            int val = 0;
            //对子串进行编码
            for (int j = i;j < i + 10 ;j ++ ) {
                val <<= 2;
                val = val | map[s.charAt(j)];
            }
            if (! ones.add(val) && twos.add(val)) {
                res.add(s.substring(i,i + 10));
            }
        }
        return res;
    }
}

④ 在discuss中看到的。

class Solution{//9ms
    public List<String> findRepeatedDnaSequences(String s){
        List<String> res = new ArrayList<>();
        if (s == null || s.length() < 10) {
            return res;
        }
        //对字母进行编码
        char[] map = new char[256];
        map['A'] = 0;
        map['C'] = 1;
        map['G'] = 2;
        map['T'] = 3;
        int mask = 0xfffff;//20bit,10个字母,每个字母占2bit
        int val = 0;
        char[] schar = s.toCharArray();
        for (int i = 0;i < 9 ;i ++ ) {//对前9位进行编码
            val = (val << 2) | (map[schar[i]] & 3);
        }
        byte[] bytes = new byte[1 << 20];
        for (int i = 9;i < schar.length ;i ++ ) {
            val = ((val << 2) & mask) | ((map[schar[i]]) & 3);//编码
            if (bytes[val] == 1) {
                res.add(String.valueOf(schar,i - 9,10));
            }
            if (bytes[val] < 2) {
                bytes[val] ++;
            }

        }
        return res;
    }
}

class Solution{//20ms ---> 可以看出,数据的类型对运行的效率也有影响
    public List<String> findRepeatedDnaSequences(String s){
        List<String> res = new ArrayList<>();
        if (s == null || s.length() < 10) {
            return res;
        }
        //对字母进行编码
        int[] map = new int[256];
        map['A'] = 0;
        map['C'] = 1;
        map['G'] = 2;
        map['T'] = 3;
        int mask = 0xfffff;//20bit,10个字母,每个字母占2bit
        int val = 0;
        char[] schar = s.toCharArray();
        for (int i = 0;i < 9 ;i ++ ) {//对前9位进行编码
            val = (val << 2) | (map[schar[i]] & 3);
        }
        int[] num = new int[1 << 20];//记录对应位置的编码出现的个数
        for (int i = 9;i < schar.length ;i ++ ) {
            val = ((val << 2) & mask) | ((map[schar[i]]) & 3);//编码
            if (num[val] == 1) {
                res.add(String.valueOf(schar,i - 9,10));
            }
            if (num[val] < 2) {
                num[val] ++;
            }
        }
        return res;
    }
}

转载于:https://my.oschina.net/liyurong/blog/1570944

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值