题目:209. 长度最小的子数组
解法: 滑动窗口,遍历右节点,左节点根据条件缩小窗口,具体见代码实现。
时间复杂度: O(n)。n 为数组 nums 的长度,while 内操作数小于等于 n,而不是每次操作 n 个,所以默认为常量。
空间复杂度: O(1)
代码实现:
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 滑动窗口
ans = inf
left = s = 0
n = len(nums)
for right, x in enumerate(nums):
s += x
while s >= target:
ans = min(right - left + 1, ans)
s -= nums[left]
left += 1
return 0 if ans == inf else ans
题目:3. 无重复字符的最长子串
解法: 思路同上,滑动窗口,遍历右指针,将搜索的字符进行计数 +1,当该字符计数 >1 时,循环缩小窗口直至无重复该字符的窗口,right - left + 1 即为当前窗口的长度,返回和上次窗口相比更大的值,具体见代码实现。
时间复杂度: O(n),n 为字符串 s 的长度
空间复杂度: O(1) 代码实现:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
cnt = Counter()
ans = left = 0
for right, c in enumerate(s):
cnt[c] += 1
while cnt[c] > 1:
cnt[s[left]] -= 1
left += 1
ans = max(right - left + 1, ans)
return ans
题目:713. 乘积小于 K 的子数组
解法: 滑动窗口,遍历右指针,获取乘积,当 s >= k 时,s/=nums[left] & left++ 循环缩小窗口直至符合条件,将所有结果进行累加 返回即可,见代码实现。特别的, k<=1 时,nums中 不存在小于 1 的子数组,返回0即可。
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k <= 1:
return 0
left = ans = 0
s = 1
for right, x in enumerate(nums):
s *= x
while s >= k:
s /= nums[left]
left += 1
ans += right - left + 1
return ans
题目:3090. 每个字符最多出现两次的最长子字符串
解法: 只需要在第3.无重复字符的代码上更改循环的条件即可,见代码实现
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def maximumLengthSubstring(self, s: str) -> int:
cnt = Counter()
ans = left = 0
for right, c in enumerate(s):
cnt[c] += 1
while cnt[c] > 2:
cnt[s[left]] -= 1
left += 1
ans = max(right - left + 1, ans)
return ans
题目:2958. 最多 K 个重复元素的最长子数组
解法: 同 3. 无重复字符的最长子串 、3090. 每个字符最多出现两次的最长子字符串
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def maxSubarrayLength(self, nums: List[int], k: int) -> int:
cnt = Counter()
left = ans = 0
for right, x in enumerate(nums):
cnt[x] += 1
while cnt[x] > k:
cnt[nums[left]] -= 1
left += 1
ans = max(right - left + 1, ans)
return ans
题目:2730. 找到最长的半重复子字符串
解法: 同3. 无重复字符的最长子串 、 3090. 每个字符最多出现两次的最长子字符串 、2958. 最多 K 个重复元素的最长子数组
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def longestSemiRepetitiveSubstring(self, s: str) -> int:
ans = left = cnt = 0
for right, c in enumerate(s):
if right and c == s[right - 1]:
cnt += 1
while cnt > 1:
out = s[left]
left += 1
if out == s[left]:
cnt -= 1
ans = max(right - left + 1, ans)
return ans
题目:1004. 最大连续1的个数 III
解法: 同3. 无重复字符的最长子串 、3090. 每个字符最多出现两次的最长子字符串、2958. 最多 K 个重复元素的最长子数组、
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
# 求连续1 的最大长度 -> 求连续 0 的最大长度
ans = left = cnt0 = 0
for right, x in enumerate(nums):
if x == 0:
cnt0 += 1
while cnt0 > k:
if nums[left] == 0:
cnt0 -= 1
left += 1
ans = max(right - left + 1, ans)
return ans
题目:2962. 统计最大元素出现至少 K 次的子数组
解法: 同2730. 找到最长的半重复子字符串
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def countSubarrays(self, nums: List[int], k: int) -> int:
mx = max(nums)
left = ans = cnt_mx = 0
for right, x in enumerate(nums):
if x == mx:
cnt_mx += 1
while cnt_mx >= k:
out = nums[left]
if out == mx:
cnt_mx -= 1
left += 1
ans += left
return ans
题目:2302. 统计得分小于 K 的子数组数目
解法: 同209. 长度最小的子数组、713. 乘积小于 K 的子数组、713. 乘积小于 K 的子数组
时间复杂度:
空间复杂度:
代码实现:
class Solution:
def countSubarrays(self, nums: List[int], k: int) -> int:
ans = left = s = 0
for right, x in enumerate(nums):
s += x
while s * (right - left + 1) >= k:
s -= nums[left]
left += 1
ans += right - left + 1
return ans
题目:1658. 将 x 减到 0 的最小操作数
解法: 从两侧减数,意味着 中心部分的 target 是连续的,可以设定target = sum(nums) - x ,求和为target的最大长度 ans,余下的就是预期的最小长度 len(nums) - ans
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
target = sum(nums) - x
if target < 0:
return -1
left = s = 0
ans = -1
for right, x in enumerate(nums):
s += x
while s > target:
s -= nums[left]
left += 1
if s == target:
ans = max(right - left + 1, ans)
return ans if ans == -1 else len(nums) - ans
题目:3795. 不同元素和至少为 K 的最短子数组长度
解法: 同 2730. 找到最长的半重复子字符串 ,但本题中的 while 循环中有坑点,① if cnt[x] == 1: s += x ② 收缩左节点时 cnt 先 -1 ,当 cnt 为 0 时,s -= out
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def minLength(self, nums: List[int], k: int) -> int:
left = s = 0
cnt = Counter()
ans = inf
for right, x in enumerate(nums):
cnt[x] += 1
if cnt[x] == 1:
s += x
while s >= k:
ans = min(right - left + 1, ans)
out = nums[left]
cnt[out] -= 1
if cnt[out] == 0:
s -= out
left += 1
return -1 if ans == inf else ans
题目:76. 最小覆盖子串
解法: 同 2730. 找到最长的半重复子字符串 、3795. 不同元素和至少为 K 的最短子数组长度
时间复杂度: O(n)
空间复杂度: O(1)
代码实现:
class Solution:
def minWindow(self, s: str, t: str) -> str:
cnt_t = Counter(t)
cnt_s = Counter()
ans_left, ans_right = -1, len(s)
left = 0
for right, c in enumerate(s):
cnt_s[c] += 1
while cnt_s >= cnt_t:
if right - left < ans_right - ans_left:
ans_left, ans_right = left, right
cnt_s[s[left]] -= 1
left += 1
return "" if ans_left == -1 else s[ans_left: ans_right + 1]