题目列表
- Leetcode 3. 无重复字符的最长子串
- Leetcode 30. 串联所有单词的子串
- Leetcode 76. 最小覆盖字串
- leetcode 209. 长度最小的子数组
- leetcode 239. 滑动窗口最大值
- Leetcode 438. 找到字符串中所有字母异位词
- Leetcode 567. 字符串的排列
- Leetcode 632. 最小区间
题目分析
滑动窗口,维护一个连续区间 [ i , j ) [i, j) [i,j),右边界不断扩展,满足一定条件时更新左边界,直至遍历整个数据。
滑动窗口的题目要明确何时更新右边界,何时更新左边界,可解决大部分求连续子串的问题。
Leetcode 3. 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
维护一个滑动窗口:
何时更新右边界? 窗口内的字符不重复。
何时更新左边界? 新进入的字符与窗口内的字符存在重复。
对于例题中的 abcabcbb,滑动窗口(窗口)左右边界的坐标初始为0,然后右边界不断扩展,直到窗口内为 abc 时满足题目要求,右边界继续扩展,此时再进入 a,窗口变成了 abca,这时候不满足要求。因此需要移动这个队列的左边界。左边界只需要不断弹出元素直到满足窗口内字符不重复,比如压入 a 时,窗口内 abc 以及包含了 a,只需要把窗口内 a 及之前的字符弹出即可。
优化:每次压入都要遍历一遍窗口判断是否有相同字符,这样最坏情况下的复杂度为 O ( n 2 ) O(n^2) O(n2) 。为了进一优化目标,可以使用一个map,记录在窗口的每个字符的位置,这样在新进入字符时只需要访问 map,即可判断窗口内是否有该字符,并且有的话可以直接获得该字符在窗口内的位置。每次进入字符时,更新窗口的最大大小,即可得到最长字串的长度。整个算法的时间复杂度变为 O ( n ) O(n) O(n)。
Leetcode 76. 最小覆盖字串
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
仍然是滑动窗口:
何时更新右边界? 窗口内的字符没有包含所有的T中的字符。
何时更新左边界? 窗口内已经包含了所有T中的字符。
滑动窗口首先不断扩展右边界,直到寻找到一个可行的子串包含T中的所有字母,然后更新左边界,不断收缩窗口来寻得满足条件的最短窗口大小。当更新左边界后窗口无法包含所有T中的字母时,再更新右边界,依次循环求解。
优化: 使用一个map记录T中每个字符需要的个数,然后更新右边界时,新进入的字符s如果是T中的字符,那么map[s]–;更新左边界时,新弹出的字符s如果是T中的字符,那么map[s]++;如果map中所有的value之和小于等于0,说明窗口内已经满足包含T中的所有字符,否则没有包含。
Leetcode 438. 找到字符串中所有字母异位词
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
- 字母异位词指字母相同,但排列不同的字符串。
- 不考虑答案输出的顺序。
输入:
s: “cbaebabacd” p: “abc”
输出:
[0, 6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的字母异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的字母异位词。
固定长度的滑动窗口。
何时更新右边界? 窗口小于p的长度。
何时更新左边界? 新进入的字符不在p中或者窗口等于p的长度。
Note:使用一个map记录p中所需字符及其个数,再使用一个map记录窗口内的字符及其个数,两者相等时即满足条件。
题目代码
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
start = 0
maxcount = 0
mapV = {}
# 右边界扩展
for i in range(len(s)):
# 是否更新左边界
start = max(start, mapV.get(s[i], -1))
# 更新新进入的字符的坐标
mapV[s[i]] = i + 1
# 更新窗口最大的大小
maxcount = max(maxcount, i - start + 1)
return maxcount
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
if len(words)==0:
return []
record = {}
for word in words:
record[word] = record.get(word, 0) + 1
winRecord = {}
start = end = 0
ans = []
length = len(words[0])
while(end<len(s)-length+1):
word = s[end:end+length]
if word in record.keys():
end += length
winRecord[word] = winRecord.get(word, 0) + 1
if end - start == len(words)*length:
if winRecord==record:
ans.append(start)
start = end = start + 1
winRecord.clear()
else:
winRecord.clear()
start = end = start + 1
return ans
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
tCount = dict()
for w in t:
tCount[w] = tCount.get(w, 0) + 1
start = 0
minStr = s + "0"
NotAllIn = any(map(lambda x: x > 0, tCount.values()))
for i in range(len(s)):
if s[i] in tCount.keys():
tCount[s[i]]-=1
NotAllIn = any(map(lambda x: x > 0, tCount.values()))
while not NotAllIn:
if i-start+1 < len(minStr):
minStr = s[start:i+1]
if s[start] in tCount.keys():
tCount[s[start]] += 1
NotAllIn = any(map(lambda x: x > 0, tCount.values()))
start = start + 1
return "" if len(minStr) > len(s) else minStr
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
words = {}
for w in p:
words[w] = words.get(w, 0) + 1
winwords = {}
ans = []
start = -1
for i in range(len(s)):
if s[i] in words.keys():
if start == -1:
start = i
winwords[s[i]] = winwords.get(s[i], 0) + 1
if i-start+1==len(p):
if winwords == words:
ans.append(start)
winwords[s[start]] -= 1
start += 1
else:
start = -1
winwords.clear()
return ans