leetcode
leetcode 151 ~ 200
重复的DNA序列

重复的DNA序列

难度:

标签:

题目描述

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'

代码结果

运行时间: 38 ms, 内存: 26.8 MB


/*
 * 思路:
 * 1. 使用Java Stream API遍历字符串s,获取所有长度为10的子字符串。
 * 2. 使用Collectors.groupingBy计算每个子字符串的出现次数。
 * 3. 过滤出出现次数大于1的子字符串并收集到结果列表中。
 */
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
public class SolutionStream {
    public List<String> findRepeatedDnaSequences(String s) {
        if (s.length() < 10) return Collections.emptyList();
        return IntStream.rangeClosed(0, s.length() - 10)
                .mapToObj(i -> s.substring(i, i + 10))
                .collect(Collectors.groupingBy(str -> str, Collectors.counting()))
                .entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }
}

解释

方法:

该题解采用滑动窗口和位操作的方法来寻找重复的DNA序列。首先,将四个可能的字符('A', 'C', 'G', 'T')映射到2位的整数(分别是0, 1, 2, 3),以便使用位操作来更新当前窗口的哈希值。使用一个长度为10的滑动窗口,遍历字符串,每向右移动窗口一位,就更新当前窗口表示的数字(通过左移两位并添加新字符的映射值,同时确保数字长度不超过20位,即10个字符的长度)。每个窗口的数字作为哈希表的键,记录每个序列出现的次数。当某个序列第二次出现时,将其添加到结果列表中。这种方法避免了直接使用字符串子串比较,而是通过计算较小的整数值来优化比较过程。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么选择长度为10的窗口来寻找重复的DNA序列?是否有特定的生物学或算法上的原因?
选择长度为10的窗口来寻找重复的DNA序列主要是因为这个长度在生物学研究中具有特定的实用意义,同时也是一个在计算上可行的长度。在生物学中,长度为10的DNA序列足够显示遗传信息的重复模式,这对于识别基因的某些特性和功能非常重要。此外,在算法设计上,长度为10使得通过位操作处理DNA序列变得高效,因为整个序列可以压缩到一个20位的整数内,这样可以使用标准的数据类型(如整型)进行快速处理,而不需要使用更复杂的数据结构。
🦆
在位操作中,你如何确保通过左移和添加新字符的值时,数字长度不会超过20位?详细解释使用的位操作和掩码。
在位操作中,以确保数字长度不超过20位,使用的关键技术是位掩码。在更新当前窗口表示的数字时,首先通过将当前值左移两位来为新字符留出空间,然后通过位或操作(|)添加新字符的映射值。为了确保这个数字不超过20位,使用一个位掩码将数字的长度限制在20位。具体操作为对数字进行与操作(&)与掩码 `((1 << 20) - 1)`。这个掩码是一个20位全为1的二进制数,这样可以确保在添加新字符后,数字的最高位(超出20位的部分)被清零,从而保持数字总是在20位以内。
🦆
为什么在DNA序列映射时选择将'A', 'C', 'G', 'T'映射到2位的整数,而不是其他位数?
选择将'A', 'C', 'G', 'T'映射到2位的整数是因为这四个字符是DNA序列的全部可能字符,而2位二进制数正好可以表示4种不同的状态(00, 01, 10, 11)。这样的映射是最为紧凑和高效的方式,因为它刚好利用了2位二进制数的全部编码能力,没有浪费。如果使用更多位数进行映射,如3位或4位,将会产生额外的未使用状态,这不仅增加了处理的复杂性,还可能导致存储效率的下降。因此,使用2位整数进行映射是一种既简洁又高效的方法。

相关问题