https://leetcode.cn/problems/minimum-recolors-to-get-k-consecutive-black-blocks/
每次找连续为
k
k
k 的连续块,然后求其中每
k
k
k 个块中白色块最少个数。
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
class Solution:
def minimumRecolors(self, blocks: str, k: int) -> int:
n = len(blocks)
ans = n
for i in range(n - k + 1):
count = 0
for j in range(i, i + k):
if blocks[j] == 'W':
count += 1
ans = min(ans, count)
return ans
定义一个
k
k
k 长度的窗口,向后滑动,若要离开窗口位置为白块,cnt--
,若要加入窗口位置为白块,cnt++
。
时间复杂度:
O
(
n
)
O(n)
O(n)
class Solution:
def minimumRecolors(self, blocks: str, k: int) -> int:
cnt = blocks[:k].count('W')
ans = cnt
for i in range(len(blocks) - k):
cnt += -(blocks[i] == 'W') + (blocks[i+k] == 'W')
ans = min(ans, cnt)
return ans
https://leetcode.cn/problems/time-needed-to-rearrange-a-binary-string/
class Solution:
def secondsToRemoveOccurrences(self, s: str) -> int:
ans = 0
while '01' in s:
s = s.replace('01', '10')
ans += 1
return ans
我们的目的是让所有的 1
都到左面,所有的 0
都到右遍变为类似 11100000
的形式。
1
前面 0
的个数表示 1
若要到对应位置所需最少的秒数1
出现表示后面的 1
需要等待前面的 1
移动后才能进行移动。class Solution:
def secondsToRemoveOccurrences(self, s: str) -> int:
f = pre0 = 0
for c in s:
if c == '0':
pre0 += 1
# '1' 前面有 '0' 才计算,没 '0' 表示已经到目标位置了,不用管
elif pre0 > 0:
f = max(f + 1, pre0)
return f
https://leetcode.cn/problems/shifting-letters-ii/
这题暴力做会超时,用差分数组会降到线性。差分数组求前缀和即为各个区间的最终变化结果。
差分数组可以把一个区间操作变为对两个数的操作,从而节省时间。
class Solution:
def shiftingLetters(self, s: str, shifts: List[List[int]]) -> str:
diff = [0] * (len(s) + 1)
for start, end, op in shifts:
op = 2 * op - 1 # 取代 if op == 0: op = -1
diff[start] += op
diff[end+1] -= op
# 计算差分数组前缀和,表示求得数组根据所给的区间求得的最后的变化
for i in range(1, len(s)):
diff[i] += diff[i-1]
# 生成字符对索引的映射, ascii_lowercase 是 string 模块的内置变量,官方已经引入
ascii_dct = {c : i for i, c in enumerate(ascii_lowercase)}
ans = []
for c, dif in zip(s, diff):
ans.append(ascii_lowercase[(ascii_dct[c] + dif) % 26])
return ''.join(ans)
https://leetcode.cn/problems/number-of-students-doing-homework-at-a-given-time/
class Solution:
def busyStudent(self, startTime: List[int], endTime: List[int], queryTime: int) -> int:
maxEndTime = max(endTime)
if queryTime > maxEndTime:
return 0
cnt = [0] * (maxEndTime + 2)
for s, e in zip(startTime, endTime):
cnt[s] += 1
cnt[e + 1] -= 1
res = []
s = 0
# 对差分数组求前缀和即为各个区间的最终变化结果
for x in cnt:
s += x
res.append(s)
return res[queryTime]
https://leetcode.cn/problems/maximum-segment-sum-after-removals/
正序为删除节点,求两边元素和。转换其为倒序,合并两边的元素。
灵神版:
class Solution:
def maximumSegmentSum(self, nums: List[int], removeQueries: List[int]) -> List[int]:
# 并查集
def find(x):
if fa[x] != x:
fa[x] = find(fa[x])
return fa[x]
n = len(nums)
ans = [0] * n
s = [0] * (n + 1)
fa = list(range(n + 1))
# 因为最后删除全部元素结果为 0,故不需要考虑
for i in range(n-1, 0, -1):
x = removeQueries[i]
to = find(x + 1)
fa[x] = to # 合并 x 和 x+1
s[to] += s[x] + nums[x]
ans[i - 1] = max(ans[i], s[to])
return ans
Y总版:
class Solution:
def maximumSegmentSum(self, nums: List[int], removeQueries: List[int]) -> List[int]:
# 并查集
def find(x):
if fa[x] != x:
fa[x] = find(fa[x])
return fa[x]
#初始化
n = len(nums)
ans = []
s = [0] * n
fa = list(range(n))
maxs = 0
# 因为最后删除全部元素结果为 0,故不需要考虑
for i in range(n-1, -1, -1):
x = removeQueries[i]
s[x] = nums[x]
for j in [x-1, x+1]:
# s[j] > 0 表示已经被加入过,该集合存在
# 将合并 x-1 和 x+1 合并到 x
if j >= 0 and j < n and s[j] > 0:
to = find(j)
s[x] += s[to]
fa[to] = x
ans.append(maxs)
maxs = max(maxs, s[x])
return ans[::-1]