DNA序列 由一系列核苷酸组成,缩写为 ‘A’, ‘C’, ‘G’ 和 ‘T’.。
例如,“ACGAATTCCG” 是一个 DNA序列 。
在研究 DNA 时,识别 DNA 中的重复序列非常有用。
给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不止一次的 长度为 10 的序列(子字符串)。你可以按 任意顺序 返回答案。
示例 1:
输入:s = “AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT”
输出:[“AAAAACCCCC”,“CCCCCAAAAA”]
示例 2:
输入:s = “AAAAAAAAAAAAA”
输出:[“AAAAAAAAAA”]
提示:
0 <= s.length <= 105
s[i]==‘A’、‘C’、‘G’ or ‘T’
解题思路:
hash+滑动窗口+二进制位运算
通过hash能够快速查找之前是否出现过,通过二进制运算可以将字符串转换成二进制整数,因为题目给的字符是四个那么就是可以用两位表示出字符 例如 A;00 C:01 G:10 T:11。窗口长度为10。
- 首先将第一个窗口计算出来,通过每次左移两位,并且 | 最新的值。将得到值写入hash中
- 窗口向右移动,左移加上新增的两位,并且通过&运算去掉头两位,具体的做法就是 (1 << 20) - 1 这样的到一个20位全是1的值,超过20位的部分都是0,&上刚刚的值就可以只保留后20位。
- 判断是否出现过,如果出现过填充字符串,注意 字符串的长度是10, 那么字符串的起点就是 end - 10 + 1,包括end本身。
代码如下:
class Solution {
public:
int helper(char s) {
if (s == 'A') return 0;
else if (s == 'C') return 1;
else if (s == 'G') return 2;
else return 3;
}
int L = 10;//长度
int Left = 2;//字符对应的二进制长度
vector<string> findRepeatedDnaSequences(string s) {
unordered_map<int,int>counts;
vector<string>res;
int end = 0, x = 0;
if (s.size() < 10) return res;
for (; end < 10; ++end) x = (x << Left) | helper(s[end]);
counts[x] = 1;
for (; end < s.size(); ++end) {
x = ((x << Left) | helper(s[end])) & ((1 << (Left * L)) - 1);
if (counts[x]++ == 1) {
res.emplace_back(s.substr(end - L + 1, L));
}
}
return res;
}
};