Leetcode——重复的DNA序列
Posted Yawn,
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
}
以上是关于Leetcode——重复的DNA序列的主要内容,如果未能解决你的问题,请参考以下文章
[LeetCode] 187. Repeated DNA Sequences 求重复的DNA序列
LeetCode 187 重复的DNA序列[字符串 Map] HERODING的LeetCode之路
Leetcode No.187 重复的DNA序列(滑动窗口)