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

🎯 问题描述(来源于LeetCode)

描述:给你一个长度为 n 的二进制字符串 blocks,它由字符 ‘B’(黑)和 ‘W’(白)组成。你需要从中找到一个长度为 k 的子字符串,并将其中所有的 ‘W’ 涂成 ‘B’。请你返回得到一个包含 至少 k 个连续 ‘B’ 的子字符串所需的最少涂色次数。

说明:

  • 涂色次数:即子字符串中 ‘W’ 字符的数量。
  • 目标:寻找长度为 k 的窗口中,‘W’ 数量最少的那个窗口。
  • 示例 1:
1
2
3
4
5
6
7
输入:blocks = "WBBWWBBWBW", k = 7
输出:3
解释:一种得到 7 个连续黑块的方案是把下标 [0, 6] 的字符重新涂色。
在这种方案下,有 3 个 'W' 需要涂成黑色。
````

- **示例 2**:

输入:blocks = “WBWBBBW”, k = 2
输出:0
解释:无需涂色,因为已经有 “BB” 子字符串了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

## 💻 解题思路

### 思路1:定长滑动窗口

#### 思路1:代码实现
```python
class Solution:
def minimumRecolors(self, blocks: str, k: int) -> int:
count = len(blocks) # 初始化为最大可能的操作次数(字符串长度)
count_num = 0 # 记录当前窗口内 'W' (白块) 的数量

for i, st in enumerate(blocks):
# 1. 扩展窗口:如果当前字符是 'W',计数加 1
if st == "W":
count_num += 1

# 计算当前窗口的左边界
left = i - k + 1

# 2. 窗口未形成:如果左边界小于 0,说明元素不足 k 个,跳过判断
if left < 0:
continue

# 3. 窗口已形成:更新最小操作次数
# 当前窗口需要的操作次数就是该窗口内 'W' 的数量
count = min(count, count_num)

# 4. 收缩窗口:如果移出窗口的最左边元素是 'W',计数减 1
if blocks[left] == "W":
count_num -= 1

return count
复杂度验证
  • 时间复杂度:O(N)
    • 只需要遍历字符串 blocks 一次。每个字符被检查一次,滑动窗口的操作是常数时间。
  • 空间复杂度:O(1)
    • 只使用了 count 和 count_num 等常数个变量,没有使用额外的数组或数据结构。

思考

  1. 问题转化:题目要求“最少涂色次数”,本质上就是寻找一个长度为 k 的子数组,其中白色块 'W' 的数量最少。这等价于寻找窗口内元素和(将 W 视为 1,B 视为 0)最小的窗口。
  2. 初始化陷阱:代码中 count 初始化为 len(blocks) 是安全的,因为最坏情况下(全是 W),最多只需要涂 k 次,而 k 一定小于等于 len(blocks)。
  3. 边界处理:
    • 代码利用 left < 0 跳过了前 k-1 个元素的判断,确保只有当窗口长度真正达到 k 时才开始计算最小值。
  4. 条件判断:
    • 这里只有当 blocks[left] 为 'W' 时才减去 count_num,因为如果是 ‘B’,它对涂色次数没有贡献,不需要从计数器中扣除。