一、题目描述
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 <= 10^5
s[i]
==
'A'
、'C'
、'G'
or'T'
二、解题思路
- 遍历字符串
s
,从索引0开始,每次截取长度为10的子字符串。 - 使用一个HashSet来存储已经遍历过的子字符串,用于快速查找重复的序列。
- 使用一个ArrayList来存储结果,即那些出现不止一次的长度为10的子字符串。
- 在遍历过程中,每截取一个长度为10的子字符串,就检查它是否在HashSet中:
- 如果不在,将其添加到HashSet中。
- 如果在,并且ArrayList中还没有这个子字符串,则将其添加到ArrayList中。
- 遍历完成后,返回ArrayList中的结果。
三、具体代码
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
class Solution {
public List<String> findRepeatedDnaSequences(String s) {
// 存储所有出现过的长度为10的子字符串
HashSet<String> seen = new HashSet<>();
// 存储重复出现的长度为10的子字符串
HashSet<String> repeated = new HashSet<>();
// 结果列表
List<String> result = new ArrayList<>();
// 遍历字符串,确保子字符串长度至少为10
for (int i = 0; i <= s.length() - 10; i++) {
// 截取长度为10的子字符串
String sequence = s.substring(i, i + 10);
// 检查子字符串是否已经出现过
if (!seen.add(sequence)) {
// 如果已经出现过,并且还没有添加到结果列表中,则添加
repeated.add(sequence);
}
}
// 将重复出现的子字符串添加到结果列表中
result.addAll(repeated);
return result;
}
}
这段代码首先创建了一个HashSet seen
来记录所有出现过的长度为10的子字符串,然后创建了一个HashSet repeated
来记录重复出现的子字符串。当遍历到重复的子字符串时,就将其添加到 repeated
中。最后,将 repeated
中的所有元素添加到结果列表 result
中并返回。注意,这里使用了 seen.add(sequence)
来判断是否已经添加过该子字符串,这个方法会返回false如果子字符串已经存在,因此如果子字符串重复,它就会被添加到 repeated
中。
四、时间复杂度和空间复杂度
1. 时间复杂度
-
循环遍历字符串
s
:由于每次循环都是固定截取长度为10的子字符串,循环次数为s.length() - 10 + 1
,因此循环的复杂度为O(n),其中n是字符串s
的长度。 -
在循环内部,每次操作都包括:
substring
操作:截取子字符串,这个操作的时间复杂度为O(1),因为子字符串的长度是固定的10。add
操作:向HashSet中添加元素,这个操作的时间复杂度平均为O(1)。
因此,总的时间复杂度为O(n),因为每次循环的操作都是常数时间复杂度,并且循环次数与字符串长度线性相关。
2. 空间复杂度
-
seen
HashSet:存储所有出现过的长度为10的子字符串,最坏情况下,每个长度为10的子字符串都不同,则seen
的大小为O(n),其中n是字符串s
的长度。 -
repeated
HashSet:存储重复出现的长度为10的子字符串,最坏情况下,所有长度为10的子字符串都重复出现,则repeated
的大小为O(n/10)。 -
result
ArrayList:存储结果,最坏情况下,所有长度为10的子字符串都重复出现,则result
的大小为O(n/10)。
综合上述,空间复杂度为O(n),因为seen
的大小与字符串长度线性相关,而repeated
和result
的大小最多也是与字符串长度线性相关。
五、总结知识点
-
类定义 (
class
关键字):- 定义了一个名为
Solution
的类。
- 定义了一个名为
-
方法定义:
- 定义了一个公共方法
findRepeatedDnaSequences
,它接受一个String
类型的参数并返回一个List<String>
。
- 定义了一个公共方法
-
集合框架:
HashSet
: 用于存储不重复的元素集合,提供了 O(1) 时间复杂度的查找性能。ArrayList
: 用于存储可重复的元素集合,支持动态数组的功能。
-
字符串操作:
substring(int beginIndex, int endIndex)
: 用于从原始字符串中截取子字符串。
-
循环结构:
- 使用
for
循环遍历字符串,计算子字符串的起始索引。
- 使用
-
条件判断:
- 使用
if
语句来检查一个子字符串是否已经存在于HashSet
中。
- 使用
-
集合操作:
add(E e)
: 向集合中添加元素,并返回一个布尔值表示是否添加成功(对于HashSet
,如果元素已存在,则返回false
)。addAll(Collection<? extends E> c)
: 将一个集合中的所有元素添加到另一个集合中。
-
泛型:
- 使用泛型来指定集合中元素的类型,例如
List<String>
和HashSet<String>
。
- 使用泛型来指定集合中元素的类型,例如
-
异常处理:
- 代码中没有显示地处理异常,但
substring
方法可能会抛出StringIndexOutOfBoundsException
如果索引不合法。
- 代码中没有显示地处理异常,但
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。