Leetcode——重复的DNA序列

1. 重复的DNA序列

在这里插入图片描述

(1)滑动窗口 + 哈希表

  • 从左到右处理字符串 ss,使用滑动窗口得到每个以 s[i]为结尾且长度为 10 的子串,同时使用哈希表记录每个子串的出现次数,如果该子串出现次数超过一次,则加入答案。
  • 为了防止相同的子串被重复添加到答案,而又不使用常数较大的 Set 结构。我们可以规定:当且仅当该子串在之前出现过一次(加上本次,当前出现次数为两次)时,将子串加入答案
class Solution {
    
    
    public List<String> findRepeatedDnaSequences(String s) {
    
    
        List<String> ans = new ArrayList<>();
        int n = s.length();
        Map<String, Integer> map = new HashMap<>();

        //遍历字符串s,i + 10 <= n时代表后续子串不足10个
        for (int i = 0; i + 10 <= n; i++) {
    
    
            //目标子串的长度为 10,且在 DNA 字符串 s 中出现次数超过一次
            String cur = s.substring(i, i + 10);
            int count = map.getOrDefault(cur, 0);
            if (count == 1) 
                ans.add(cur);
            map.put(cur, count + 1);
        }
        
        return ans;
    }
}

(2)自定义哈希

先思考一下慢在什么地方?

  • 一是每次生成子串都是新的字符串(Java中字符串不可变)
  • 二是计算hash每次都是10个字符全部遍历一遍。

那么,我们有没有办法规避掉这两个问题呢?

  • 首先,我们可以不生成子串,直接使用字符来计算hash。
  • 其次,我们可以设计一个滑动窗口为10的窗口,每次向前滑动时把第一个字符删掉,加入一个新的字符就可以了

要实现以上两点,我们需要自定义我们的hash方法。

  • 考虑到题目约定了一共只会出现 ACGT 四种字符,所以,我们可以使用把 ACGT 四个字符映射到 2 位的数字上,分别用二进制的 00、01、10、11 表示,即 0、1、2、3,这样长度为 10 的子串一共只需要 20 位,用一个 int 类型就可以承载了。
class Solution {
    
    

    // 把ACGT四个字符映射到2位的数字
    static int[] MASK_MAP = new int[26];
    static {
    
    
        MASK_MAP['A' - 'A'] = 0;
        MASK_MAP['C' - 'A'] = 1;
        MASK_MAP['G' - 'A'] = 2;
        MASK_MAP['T' - 'A'] = 3;
    }

    public List<String> findRepeatedDnaSequences(String s) {
    
    
        int n = s.length();
        List<String> ans = new ArrayList<>();
        if (n <= 10) {
    
    
            return ans;
        }

        // 记录每个hash出现的次数
        int[] map = new int[1 << 20];
        int hash = 0;
        for (int i = 0; i < 10; i++) {
    
    
            // 每2位一个字符
            hash = hash << 2 | MASK_MAP[s.charAt(i) - 'A'];
        }
        map[hash]++;

        for (int i = 1; i <= n - 10; i++) {
    
    
            // & 0xfffff 表示打掉最高位的2位
            hash = (hash << 2 | MASK_MAP[s.charAt(i + 10 - 1) - 'A']) & 0xfffff;
            map[hash]++;
            // 因为不存在hash冲突,所以可以直接使用
            if (map[hash] == 2) {
    
    
                ans.add(s.substring(i, i + 10));
            }
        }

        return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/ly0724ok/article/details/120664931