[LeetCode 187.] 重复的DNA序列

LeetCode 187. 重复的DNA序列

一道滚动哈希的题目,注意运算符优先级问题。

题目描述

所有 DNA 都由一系列缩写为 'A','C','G' 和 'T' 的核苷酸组成,例如:"ACGAATTCCG"。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。

编写一个函数来找出所有目标子串,目标子串的长度为 10,且在 DNA 字符串 s 中出现次数超过一次。

示例 1:

输入:s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"
输出:["AAAAACCCCC","CCCCCAAAAA"]

示例 2:

输入:s = "AAAAAAAAAAAAA"
输出:["AAAAAAAAAA"]

提示:

  • 0 <= s.length <= 105
  • s[i] 为 'A'、'C'、'G' 或 'T'

解题思路

注意到题目中,要匹配的串长度固定10字符,并且字符只有4个,我们完全可以用一个 uint32 来编码每个待匹配串,只需要 20bit 就足够了。
由于串的长度比较小,这里我们用 hashmap 来统计串出现的次数,更多的话可以考虑直接开辟一个大小为 2^20 的数组来计数。
特别要注意的是,先移位后加减的运算,移位操作必须使用括号,不然会先算加减后算移位!!!

参考代码

class Solution {
    uint32_t enc(char c) {
        switch(c) {
            case 'A': return 0;
            case 'C': return 1;
            case 'G': return 2;
            case 'T': return 3;
            default: assert(false);
        }
    }
    string dec(uint32_t hash) {
        const char maps[] = "ACGT";
        string res(10, ' ');
        for (int i=0; i<10; i++) {
            res[i] = maps[(hash >> (18 - i*2)) & 0x3];
        }
        return res;
    }
public:
    vector<string> findRepeatedDnaSequences(string s) {
        if (s.size() <= 10) return {};
        // <hash-key, count>
        unordered_map<uint32_t, int> cnt;
        uint32_t hash = 0;
        for (int i=0; i<10; i++) {
            hash = (hash << 2) + enc(s[i]);
        } // 坑在于加法优先级高于移位,所以移位必须要加括号
        cnt[hash] = 1;
        for (int i=10; i<s.size(); i++) {
            hash = ((hash << 2) + enc(s[i])) & 0xFFFFF;
            cnt[hash]++;
            // cout << hash << ' ' << dec(hash) << ' ' << cnt[hash] << endl;
        }
        vector<string> res;
        for (auto [k, v] : cnt) {
            // cout << dec(k) << ' ' << v << endl;
            if (v > 1) {
                res.emplace_back(dec(k));
            }
        }
        return res;
    } // AC
};
posted @ 2021-10-08 16:21  与MPI做斗争  阅读(47)  评论(0编辑  收藏  举报