[英雄星球六月集训LeetCode解题日报] 第六日 滑动窗口
一、 1984. 学生分数的最小差值
链接: 1984. 学生分数的最小差值
1. 题目描述
给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k 。
从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分 和 最低分 的 差值 达到 最小化 。
返回可能的 最小差值 。
2. 思路分析
显然可以排序让离得近的分数靠一起然后滑动窗口走起。
3. 代码实现
class Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
n = len(nums)
nums.sort()
i,j = 0,k-1
ans = nums[j]-nums[i]
for j in range(k,n):
i += 1
ans = min(ans, nums[j]-nums[i])
if ans == 0:
return ans
return ans
二、 1763. 最长的美好子字符串
链接: 1763. 最长的美好子字符串
1. 题目描述
当一个字符串 s 包含的每一种字母的大写和小写形式 同时 出现在 s 中,就称这个字符串 s 是 美好 字符串。比方说,“abABB” 是美好字符串,因为 ‘A’ 和 ‘a’ 同时出现了,且 ‘B’ 和 ‘b’ 也同时出现了。然而,“abA” 不是美好字符串因为 ‘b’ 出现了,而 ‘B’ 没有出现。
给你一个字符串 s ,请你返回 s 最长的 美好子字符串 。如果有多个答案,请你返回 最早 出现的一个。如果不存在美好子字符串,请你返回一个空字符串。
2. 思路分析
这题算简单就离谱,想了半天。
从大到小枚举滑窗长度,遇到美丽数组就返回。
判断美丽数组:
- 滑窗移动时,维护每个字母的数量 。
- 右侧新入字母判断是否有兄弟字符在,如果有,记录这对字符进一个set。
- 那么当这个set里包含所有窗口里的字符就是符合题意的情况。
- O(n2)
3. 代码实现
class Solution:
def longestNiceSubstring(self, s: str) -> str:
n = len(s)
for k in range(n,1,-1):
i,j = 0,k-1
visited = set() # 含兄弟字符的字符
cnt = collections.defaultdict(int) # 字符计数
for x in range(i,k):
c = s[x]
cnt[c] += 1
d = chr(ord(c)^32) # c的兄弟字符
if d in cnt and cnt[d] > 0:
visited.add(c)
visited.add(d)
if len(visited) == len(cnt):
return s[i:j+1]
# print(k,cnt,visited)
for j in range(k,n):
# 滑窗右移,删除左边一个元素
c = s[i]
cnt[c] -= 1
if cnt[c] == 0 :
visited.discard(c)
visited.discard(chr(ord(c)^32))
del cnt[c]
i += 1
# 增加右边一个元素
c = s[j]
d = chr(ord(c)^32)
cnt[c] += 1
if d in cnt and cnt[d] > 0:
visited.add(c)
visited.add(d)
if len(visited) == len(cnt):
return s[i:j+1]
# print(k,cnt,visited)
return ''
三、 2269. 找到一个数字的 K 美丽值
1. 题目描述
一个整数 num 的 k 美丽值定义为 num 中符合以下条件的 子字符串 数目:
子字符串长度为 k 。
子字符串能整除 num 。
给你整数 num 和 k ,请你返回 num 的 k 美丽值。
注意:
允许有 前缀 0 。
0 不能整除任何值。
一个 子字符串 是一个字符串里的连续一段字符序列。
2. 思路分析
直接模拟。
3. 代码实现
class Solution:
def divisorSubstrings(self, num: int, k: int) -> int:
s = str(num)
ans = 0
for j in range(k-1,len(s)):
a = int(s[j-k+1:j+1])
if a>0 and num%a==0:
ans += 1
return ans
四、995. K 连续位的最小翻转次数
1. 题目描述
给定一个二进制数组 nums 和一个整数 k 。
k位翻转 就是从 nums 中选择一个长度为 k 的 子数组 ,同时把子数组中的每一个 0 都改成 1 ,把子数组中的每一个 1 都改成 0 。
返回数组中不存在 0 所需的最小 k位翻转 次数。如果不可能,则返回 -1 。
子数组 是数组的 连续 部分。
2. 思路分析
困难题。
参考: [LeetCode解题报告] 995. K 连续位的最小翻转次数
用队列维护前边窗口翻转的左端点,那么可以通过队列长度来判断自己现在的值。
或者用区间更新单点询问的模型也能做。
3. 代码实现
class Solution:
def minKBitFlips(self, nums: List[int], k: int) -> int:
n = len(nums)
q = deque()
ans = 0
for i in range(n-k+1):
if q and q[0] <= i-k:
q.popleft()
if nums[i] ^ (len(q)&1) == 1:
continue
ans += 1
q.append(i)
for i in range(n-k+1,n):
if q and q[0] <= i-k:
q.popleft()
if nums[i] ^ (len(q)&1) == 0:
return -1
return ans