文章目录
- Tag
- 题目来源
- 题目解读
- 解题思路
- 方法一:哈希表
- 方法二:哈希表+滑动窗口+位运算
- 写在最后
Tag
【哈希表】【位运算+滑动窗口+哈希表】【字符串】【2023-11-05】
题目来源
187. 重复的DNA序列
题目解读
找出字符串中重复出现的字符串。
解题思路
方法一:哈希表
一种朴素的方法是枚举所有长度为 10
的子字符串,并用哈希表计数,如果该子字符串的出现的次数超过一次,那么该子字符串就是答案要求的重复的 DNA 序列。
实现代码
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
unordered_map<string, int> cnts;
int n = s.size();
vector<string> res;
for (int i = 0; i <= n - 10; ++i) {
string sub = s.substr(i, 10);
if (++cnts[sub] == 2) {
res.push_back(sub);
}
}
return res;
}
};
复杂度分析
时间复杂度:
O
(
N
⋅
L
)
O(N \cdot L)
O(N⋅L),
N
N
N 为字符串的 s
的长度,
L
L
L 为固定的字符串的大小。
空间复杂度: O ( N ) O(N) O(N)。
方法二:哈希表+滑动窗口+位运算
实际上我们可以使用一个固定大小的滑窗来记录长度为 10
的字符串。题目中已告知我们字符串中的字符种类只有四种,因此可以使用 2
个比特来表示每个字符,于是可以有:
- A 表示为二进制为
00
; - C 表示为二进制为
01
; - G 表示为二进制为
10
; - T 表示为二进制为
11
;
这样的话长度为 10
的字符串就可以使用 20
为比特来表示即一个 int 型整数,也就是说滑窗内的字符串可以使用一个 int 型整数表示,这样就降低了时间复杂度。具体地:
- 先向滑窗中塞进去
9
个字符; - 滑窗每向右移动一位,滑窗内就会增加一个字符,滑窗最左侧的字符离开窗口:
- 滑窗每向右移动一位,滑窗内就会增加一个字符,表示滑窗内字符串的二进制整数值
x = x << 2
,x = x | bin[ch]
,bin[ch]
表示字符ch
对应的二进制; - 滑窗左侧的字符离开窗口,
x = x & ((1 << 20) - 1)
,因此时x
有22
位比特,我们只需要低20
位,所以与上(1 << 20) - 1
;
- 滑窗每向右移动一位,滑窗内就会增加一个字符,表示滑窗内字符串的二进制整数值
- 剩下就是更新哈希表,一旦有第二次出现的
x
,则表示的字符串就是重复的 DNA 序列。
实现代码
class Solution {
public:
unordered_map<char, int> bin = {{'A', 0}, {'C', 1}, {'G', 2}, {'T', 3}};
vector<string> findRepeatedDnaSequences(string s) {
vector<string> res;
int n = s.size();
if (n <= 10) {
return res;
}
int x = 0;
for (int i = 0; i < 9; ++i) {
x = (x << 2) | bin[s[i]];
}
unordered_map<int, int> cnts;
for (int i =0; i <= n - 10; ++i) {
x = ((x << 2) | bin[s[i + 10 - 1]]) & ((1 << 20) - 1);
if (++cnts[x] == 2) {
res.push_back(s.substr(i, 10));
}
}
return res;
}
};
复杂度分析
时间复杂度:
O
(
N
)
O(N)
O(N),
N
N
N 为字符串的 s
的长度,
L
L
L 为固定的字符串的大小。
空间复杂度: O ( N ) O(N) O(N)。
写在最后
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。
如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。
最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。