思路:滑动窗口 + 位运算
- 滑动窗口的关键在于,利用旧序列和新序列的相似性降低运算的复杂度
- 此题中只有4个字母,那么可以使用2位
bit
来记录每一个字母 - 滑动窗口一入一出,更新记录当前的10个字母的整数
res_bit
- 如何把右边的读进来?1.左移两位
res_bit <<= 2
2.把右边的读进来 res_bit |= to_bin[ch]
- 如何把左边的弹出去?希望最左边的两位(左数第21 22两位)变成0,也就是希望 & 0111111… 其中,0处在第20位上,
x & ((1 << 20) - 1)
- 哈希表里面的key就是位运算的结果x, value就是出现的次数
- 如何返回需要的字符串呢?需要边读边更新哈希表,如果出现的次数 >= 2,说明应该返回此时的位置了
代码:
class Solution:
def __init__(self):
self.L = 10
self.to_bin = {'A': 0, 'C': 1, 'G': 2, 'T': 3}
def findRepeatedDnaSequences(self, s: str) -> List[str]:
'''
位运算:
* 每个数字用两位的二进制字符来表示
* 如何把右边的读进来?1.左移两位 x <<= 2 2.把右边的读进来 x |= to_bin[ch]
* 如何把左边的弹出去?希望最左边的两位(左数第21 22两位)变成0,也就是希望 & 0111111... 其中,0处在第20位上
即 x & ((1 << 20) - 1)
滑动窗口 + 哈希表:
* 哈希表里面的key就是位运算的结果x, value就是出现的次数
* 如何返回需要的字符串呢?需要边读边更新哈希表,如果出现的次数 >= 2,说明应该返回此时的位置了
'''
len_s = len(s)
# 读掉边缘情况
if len_s <= 10:
return []
cnt_str = collections.defaultdict(int)
list_str = list()
# 创建第一个滑动窗口
bit_res = 0
for i in range(self.L):
bit_res = (bit_res << 2) | self.to_bin[s[i]]
cnt_str[bit_res] += 1
# 滚动更新哈希表和滑动窗口
for i in range(self.L, len_s):
# 右边的移动进来
bit_res = ((bit_res << 2) | self.to_bin[s[i]])
# 左边的移动出去
bit_res = (bit_res & ((1 << (2 * self.L)) - 1))
# 更新哈希表
cnt_str[bit_res] += 1
# 如果次数 >= 2
if cnt_str[bit_res] == 2:
list_str.append(s[i - self.L + 1: i + 1])
return list_str