leetcode 3 无重复字符的最长子串
题目链接
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
解题思路与代码思路:
滑动窗口
要满足的条件有三:
- 不含有重复字符
- 最长的子串
- 返回长度
思路是遍历字符串,扩大窗口右端,同时把字母存进字典,直到遇见重复元素就缩小窗口左端;每次扩大右端都要查看之前的子串大小与当前窗口的大小比较,取最大值;当遍历完整个字符串,则任务完成。
利用集合解决
依次递增枚举子串起始位置,那么子串的结束位置也是递增的。假设k为起始位置,rk为结束位置,那么从k+1到rk的字符肯定不重复的,所以可以开始增大rk,直到遇到重复字符。
使用哈希集合来判断重复字符,左指针移动时就从集合移除一个字符,右指针移动时就添加字符进入集合
代码:
滑动窗口(这种写法比较直观好理解)
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
window = {} #用于判断重复元素
left,right = 0,0
res = 0
while right < len(s): #右指针代表遍历完整个字符串
c = s[right] #取右指针元素
right += 1 #扩大窗口
window[c] = window.get(c,0) + 1
#判断是否要缩小窗口
while window[c] > 1 :
d = s[left]
left += 1
window[d] -= 1
res = max(res,right - left)
return res
利用集合解决(这种写法仅仅看看)
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# 哈希集合,记录每个字符是否出现过
occ = set()
n = len(s)
# 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
rk, ans = 0, 0
for i in range(n):
if i != 0:
# 左指针向右移动一格,移除一个字符
occ.remove(s[i - 1])
while rk < n and s[rk] not in occ:
# 不断地移动右指针
occ.add(s[rk])
rk += 1
# 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i)
return ans
复 杂 度 分 析 : \color{red}{复杂度分析:} 复杂度分析:
- 滑动窗口
- 时间复杂度:O(N),其中 N 是字符串的长度。左指针和右指针分别会遍历整个字符串一次。
- 空间复杂度:OO(∣Σ∣),其中 Σ 表示字符集(即字符串中可以出现的字符)
leetcode 424 替换后的最长重复字符
题目链接
https://leetcode-cn.com/problems/longest-repeating-character-replacement/
解题思路与代码思路:
滑动窗口
字符串限制在大写英文字母,要找到包含重复字母的最长子串,有一个灵活的点:至少可以替换任意位置字符K次。
延用上面的框架,那就是在扩大窗口的同时,添加K次的替换操作,做到当前最大重复;不满足【窗口大小-最多重复元素<=k】,那么左指针移动;
代码:
伪代码
# 替换后长度最长的字符串
# 能够 替换 k 个·
# 找出当前窗口最长的那个
# 把其他的替换掉
# 最多可替换k,那就替换k次
# AABABBA
# k = 1
# A 不需要替换
# AA, 不需要替换
# AAB, max_fre_num 是 A,有一个不是 A, 1 <= K, replace B -> AAA
# AABA, max_fre_num 是 A, 有一个不是 A, 1 <= k, replace B -> AAAA
# AABAB, 剩下的空位k没办法解决了, shrink window
# ABAB,shrink
# BAB, max_fre_num 是B, BBB
# BABB, BBBB
# BABBA, 剩下的空位k没办法解决了, shrink window
# window_length - max_fre_length <= k , maxlength = window_length
# else shrink
实现代码
res = 0
dic = {}
left = 0
for right,val in enumerate(s):
dic[val] = dic.get(val,0) + 1 # 当前重复最多的字母个数
while right - left + 1 - max(dic.values()) > k: # 窗口翻转k个字符后是否还有别的字符
dic[s[left]] -= 1
left += 1
res = max(res,right-left+1)
return res
复 杂 度 分 析 : \color{red}{复杂度分析:} 复杂度分析:
- 滑动窗口
- 时间复杂度:O(n)
- 空间复杂度:O(1),26个大写英文字母
leetcode 209 长度最小子数组
题目链接
https://leetcode-cn.com/problems/minimum-size-subarray-sum/
解题思路与代码思路:
滑动窗口
要满足的条件是:
- 总和大于等于s
- 长度最小的连续子数组
- 返回长度
利用前后指针一起移动的方式;从第一个元素出发,右指针一直移动,直到总和大于等于s时,记录当前子数组长度,减掉子数组第一个值,移动左指针,继续循环。
写循环时要注意先后顺序,然后记录结果的result变量是个关键点,可以通过它来判断最后的结果判断, l e n ( n u m s ) + 1 len(nums)+1 len(nums)+1是因为不可能存在result大于nums长度,不写 l e n ( n u m s ) len(nums) len(nums)的原因主要是避免刚好result就是整个数组的和
代码:
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
if nums is None or len(nums) == 0:
return 0
result = len(nums)+1
total = 0
i,j = 0,0
while j < len(nums):
total += nums[j]
j+=1
while total >= s:
result = min(result,j-i)
total -= nums[i]
i+=1
return 0 if result == len(nums)+1 else result
复 杂 度 分 析 : \color{red}{复杂度分析:} 复杂度分析:
- 滑动窗口
- 时间复杂度:O(n),其中 n是数组的长度。指针最多各移动 n次
- 空间复杂度:O(1)