[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
};