题目
题目描述:给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
提示:
1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母
思路
本质是滑动窗口问题。窗口大小即为字符串 p 的大小,然后从左至右遍历 s 字符串,每次比较当前滑动窗口是否与 p 一样,如果一样则将当前滑动窗口第一个字符串在 s 中的位置添加至返回的列表中。
这题的重点就是如何比较滑动窗口与 p 是否一样。这里我尝试了两个方法:
1. 因为我是用的python,第一时间就想到了直接将滑动窗口和 p 转换为list,然后比较 sorted(list(滑动窗口)) == sorted(list(p))。这是比较容易理解的一个方法,不过可能是因为循环的每一步都用了sorted(),所以提交之后发现时间较慢。
2. 看了几个高赞题解,理解了之后试了第二个方法,时间确实快一些。这个方法声明了两个长度为26(26个字母)的全0列表,然后将滑动窗口和 p 中的字符出现的频次记录在这两个列表中。最后直接比较这两个列表是否相同。其中滑动窗口每向右滑动一个元素,则将该元素在 s 对应的列表中的频次减1,代表该元素移出滑动窗口。接着再将新挪进滑动窗口的元素频次加1。
Python3代码
方法一:(利用sorted()函数)
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
result = []
l = 0
p_li = sorted(list(p))
while l <= len(s) - len(p): # if s 剩下的字符串长度 < p 字符串长度,就不需要比较了,因为肯定不是子串
s_li = sorted(s[l: l + len(p)]) # 当前滑动窗口
if s_li == p_li: # 比较当前窗口是否等于 p
result.append(l)
l += 1
return result
方法二:(时间更快)
- 时间复杂度:。其中N为字符串 s 的长度。
- 空间复杂度:。
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
n, m = len(s), len(p)
s_li = [0] * 26
p_li = [0] * 26
result = []
if n < m:
return result
for i in range(m):
s_li[ord(s[i]) - 97] += 1 # 97 = ord('a')
p_li[ord(p[i]) - 97] += 1
if s_li == p_li:
result.append(0)
for i in range(m, n):
s_li[ord(s[i - m]) - 97] -= 1 # 将滑动窗口的第一个字符去掉(滑动窗口向右滑动一位)
s_li[ord(s[i]) - 97] += 1 # 添加滑动后的滑动窗口中最右边的字符
if s_li == p_li:
result.append(i - m + 1) # 将滑动窗口的第一个字符所在位置添加进result中
return result