目录
题目描述
示例1:
示例2:
提示:
解题思路
滑动窗口法
概念
应用场景及特点:
思路
流程展示
代码
复杂度分析
题目描述
给你一个长度为 n 下标从 0 开始的字符串 blocks
,blocks[i]
要么是 'W'
要么是 'B'
,表示第 i
块的颜色。字符 'W'
和 'B'
分别表示白色和黑色。
给你一个整数 k
,表示想要 连续 黑色块的数目。
每一次操作中,你可以选择一个白色块将它 涂成 黑色块。
请你返回至少出现 一次 连续 k
个黑色块的 最少 操作次数。
示例1:
输入:blocks = "WBBWWBBWBW", k = 7
输出:3
解释:
一种得到 7 个连续黑色块的方法是把第 0 ,3 和 4 个块涂成黑色。
得到 blocks = "BBBBBBBWBW" 。
可以证明无法用少于 3 次操作得到 7 个连续的黑块。
所以我们返回 3 。
示例2:
输入:blocks = "WBWBBBW", k = 2
输出:0
解释:
不需要任何操作,因为已经有 2 个连续的黑块。
所以我们返回 0 。
提示:
- n == blocks.length
- 1 <= n <= 100
- blocks[i] 要么是 'W' ,要么是 'B' 。
- 1 <= k <= n
解题思路
滑动窗口法
概念
滑动窗口是一个在序列上移动的区间,通常由左右两个指针来界定这个区间的范围。通过移动指针来改变窗口的大小和位置,在窗口移动的过程中,根据问题的需求进行特定的计算和处理。
应用场景及特点:
- 子数组 / 子串问题:
- 通常有两个指针,一个指向窗口的左端,一个指向窗口的右端。根据问题的具体要求,以特定的方式移动指针。
- 例如,在寻找满足特定条件的最小子数组时,可能会先扩大窗口直到满足条件,然后再缩小窗口以找到最小的满足条件的窗口。
- 指针移动规则:
- 相比于暴力枚举所有可能的子数组 / 子串,滑动窗口法通常能够在更短的时间内找到解。因为它利用了子数组 / 子串的连续性和窗口的滑动特性,避免了重复计算。
- 高效性:
- 当需要在一个序列中找到满足特定条件的连续子数组或子串时,滑动窗口非常适用。例如,寻找和为特定值的连续子数组、含有特定字符的最长子串等。
- 窗口的大小通常是动态变化的,根据问题的条件进行调整。
思路
- 初始化:
- 首先计算初始窗口(前
k
个字符)中白色块的数量,这个数量就是把这部分变成连续k
个黑色块所需的最少操作次数。记为cnt_w
(初始窗口中白色块的数量)和ans
(最少操作次数)。
- 滑动窗口遍历:
- 使用滑动窗口遍历整个字符串。窗口大小为
k
。 - 每次窗口向右移动一个字符,新进入窗口的字符记为
in_
,离开窗口的字符记为out
。 - 如果新进入窗口的字符是
'W'
,则将cnt_w
加 1,表示需要多一次操作把这个白色块变成黑色。 - 如果离开窗口的字符是
'W'
,则将cnt_w
减 1,表示这个白色块不再在窗口内,之前为了它进行的操作可以不算了。 - 在每次窗口移动后,更新
ans
为当前ans
和cnt_w
的较小值,因为我们要找的是最少操作次数。
流程展示
代码
class Solution:
def minimumRecolors(self, blocks: str, k: int) -> int:
ans = cnt_w = blocks[:k].count('W')
for in_, out in zip(blocks[k:], blocks):
cnt_w += (in_ == 'W') - (out == 'W')
ans = min(ans, cnt_w)
return ans
复杂度分析
- 时间复杂度:O(n),其中 n 为 blocks 的长度。
- 空间复杂度:O(1),仅用到若干额外变量。