题目描述:所有 DNA 由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。
编写一个函数来查找 DNA 分子中所有出现超多一次的10个字母长的序列(子串)。
示例:
输入: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT" 输出: ["AAAAACCCCC", "CCCCCAAAAA"]
解法1。利用python里对字符串切片和字典的便利性,使用一个字典存储出现过的长度为10的子串,一次只取长度为10的子串
class Solution:
def findRepeatedDnaSequences(self, s):
"""
:type s: str
:rtype: List[str]
"""
if len(s)<10:
return []
else:
res = []
appeared = {}
for i in range(len(s)-9): # 注意次数的上限,经过验算是len(s)-9而非len(s)-10
tmp = s[i:i+10]
if tmp not in appeared:
appeared[tmp] = 1
elif appeared[tmp] == 1: # 在tmp出现2次时放到res里,之后就不放,避免重复
res.append(tmp)
appeared[tmp] += 1
return res
解法2。位运算的方法,其实就是将键值由字符串转译成了二进制int数,节省空间和时间。
- 此题由于构成输入字符串的字符只有四种,分别是A, C, G, T,下面我们来看下它们的ASCII码用二进制来表示:
- A: 0100 0001
- C: 0100 0011
- G: 0100 0111
- T: 0101 0100
- 所以对每个字符得到其ord('')后&7获得后3位,这样得到一个30位长的键值,存在字典,若出现2次就放到res里
- 初始化就先获得前9个字符的后三位,例如取出前9个字符AAAAACCCC,根据上面的分析,我们用三位来表示一个字符,所以这9个字符可以用二进制表示为001001001001001011011011011,然后从第10个字符开始遍历,取后27位(后9个字符)再或新的字符后3位,实现将当前字符加进来和第一个最先出现的字符去掉
class Solution:
def findRepeatedDnaSequences(self, s):
"""
:type s: str
:rtype: List[str]
"""
if len(s)<10:
return []
else:
res = []
appeared = {}
mask = 0x7ffffff
cur = 0
for i in range(9):
cur = (cur<<3) | (ord(s[i])&7)
for i in range(9,len(s)):
cur = (cur&mask)<<3 | (ord(s[i])&7)
if cur not in appeared:
appeared[cur] = 1
else:
if appeared[cur] == 1:
res.append(s[i-9:i+1])
appeared[cur] += 1
return res