[LeetCode85双周赛] [滑动窗口] [差分数组] [并查集]

LeetCode 6156. 得到 K 个黑块的最少涂色次数

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

LeetCode 6157. 二进制字符串重新安排顺序需要的时间

https://leetcode.cn/problems/time-needed-to-rearrange-a-binary-string/

模拟(调用 API)

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

LeetCode 6158. 字母移位 II

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)

补充类似题目(差分数组) LeetCode 1450. 在既定时间做作业的学生人数

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]

LeetCode 6159. 删除操作后的最大子段和

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]

宫水三叶姐的 Github

https://github.com/SharingSource/LogicStack-LeetCode/wiki

你可能感兴趣的:(算法,leetcode,算法,动态规划)