力扣第 3654 题《First Step》是一个典型的二维矩阵遍历与统计问题,核心目标是在一个 R×C 的矩阵中,统计所有长度为 K 的连续空位(用 '.' 表示)的数量,统计方向包括横向(从左到右)和纵向(从上到下) ,但同一段连续空位不应在横向和纵向被重复计数。
问题解构
- 输入 :
R:矩阵行数。C:矩阵列数。K:需要统计的连续空位长度。- 一个
R×C的字符矩阵,包含 '.'(空位)和 '#'(障碍物)。
- 输出 :
- 一个整数,表示所有长度为
K的连续空位段的数量。
- 一个整数,表示所有长度为
- 关键约束 :
K可能为 1。当K=1时,每个 '.' 单元格自身就是一个长度为 1 的连续段。- 必须分别沿行(横向)和列(纵向)进行统计。
- 避免重复计数。例如,一个 '.' 单元格在横向统计时被计入,在纵向统计时不应再因其自身被计入。
方案推演
最直接的思路是双重循环遍历矩阵的每个单元格,并以其为起点,分别向右(横向)和向下(纵向)检查连续的空位数量。
-
横向统计:
- 遍历每一行
i(0 <= i < R)。 - 对于该行的每一列
j(0 <= j < C),如果grid[i][j]是 '.',则尝试向右扩展。 - 从
j开始,连续检查K个单元格(grid[i][j]到grid[i][j+K-1])。 - 如果这
K个单元格都在矩阵范围内且都是 '.',则找到一个有效的横向连续段,计数器加一。 - 优化 :由于我们按行遍历,且从左到右检查,实际上我们是在统计所有可能的、长度为
K的、起始于(i, j)的横向窗口。对于同一行,当j增加时,窗口滑动,可以利用之前的信息,但本题数据规模通常允许简单的逐段检查。
- 遍历每一行
-
纵向统计:
- 逻辑与横向完全对称,但遍历顺序变为先列后行。
- 遍历每一列
j(0 <= j < C)。 - 对于该列的每一行
i(0 <= i < R),如果grid[i][j]是 '.',则尝试向下扩展。 - 从
i开始,连续检查K个单元格(grid[i][j]到grid[i+K-1][j])。 - 如果这
K个单元格都在矩阵范围内且都是 '.',则找到一个有效的纵向连续段,计数器加一。
-
处理 K=1 的重复计数:
- 当
K=1时,上述横向和纵向统计逻辑都会将每个 '.' 单元格单独计为一段。 - 根据题意,同一段连续空位不应在横向和纵向被重复计数。对于
K=1,一个 '.' 单元格本身就是一个"段",它不应该被既算作横向段又算作纵向段。 - 因此,当且仅当
K=1时,最终的答案应为横向统计数或纵向统计数中的一个 ,因为此时两者相等,且都等于矩阵中 '.' 的数量。或者更简单地说,当K=1时,答案就是 '.' 的数量。 - 对于
K>1,横向段和纵向段在空间上几乎不可能完全重合(除非是特殊的 L 形或十字形,但题目定义的连续是直线,所以不会重复),因此可以直接将横向和纵向的计数相加。
- 当
代码实现 (Python)
以下是根据上述推演实现的 Python 代码,包含详细注释。
python
def first_step(R, C, K, grid):
"""
统计矩阵中长度为K的连续空位数量。
:param R: 行数
:param C: 列数
:param K: 连续空位长度
:param grid: 二维字符列表,表示矩阵
:return: 连续空位段的数量
"""
if K == 1:
# 特殊情况:K=1,答案就是所有 '.' 的数量
count = 0
for i in range(R):
for j in range(C):
if grid[i][j] == '.':
count += 1
return count
total_count = 0
# 1. 横向统计(按行遍历)
for i in range(R):
for j in range(C):
if grid[i][j] == '.':
# 检查从 (i, j) 开始向右的K个单元格
valid = True
# 首先检查右边界是否越界
if j + K - 1 >= C:
valid = False
else:
for step in range(K):
if grid[i][j + step] != '.':
valid = False
break
if valid:
total_count += 1
# 2. 纵向统计(按列遍历)
for j in range(C):
for i in range(R):
if grid[i][j] == '.':
# 检查从 (i, j) 开始向下的K个单元格
valid = True
# 首先检查下边界是否越界
if i + K - 1 >= R:
valid = False
else:
for step in range(K):
if grid[i + step][j] != '.':
valid = False
break
if valid:
total_count += 1
return total_count
# 示例输入与测试
if __name__ == "__main__":
# 示例1: 参考常见测试用例
R, C, K = 3, 5, 2
grid = [
['#', '.', '.', '.', '#'],
['.', '.', '#', '.', '.'],
['#', '.', '.', '.', '#']
]
# 手动分析:
# 横向:第0行,位置(0,1)开始的".."有效,位置(0,2)开始的".."有效(但起点(0,2)的段与(0,1)的段重叠,题目要求统计所有可能的起始位置,因此都算)。
# 第1行,位置(1,0)开始的".."有效,位置(1,3)开始的".."有效。
# 第2行,位置(2,1)开始的".."有效,位置(2,2)开始的".."有效。
# 横向总计:2 + 2 + 2 = 6。
# 纵向:第1列,位置(0,1)开始的". ."(中间有障碍?这里(0,1)是'.', (1,1)是'.', (2,1)是'.',连续三个'.',但我们需要长度为2。从(0,1)开始向下2个是有效的,从(1,1)开始向下2个也是有效的)。
# 需要仔细计算每个列为起点的向下连续段。为了清晰,我们用代码验证。
print("示例1 结果:", first_step(R, C, K, grid))
# 示例2: K=1 的情况
R2, C2, K2 = 2, 2, 1
grid2 = [
['.', '#'],
['#', '.']
]
# 矩阵中有两个 '.',K=1时答案应为2。
print("示例2 (K=1) 结果:", first_step(R2, C2, K2, grid2))
复杂度分析
- 时间复杂度 :
O(R * C * K)。最坏情况下,我们需要遍历每个单元格(R*C),并对每个单元格可能进行最多K次检查(虽然通过提前判断越界可以跳过很多,但渐进复杂度如此)。对于本题常见的数据范围(如 R, C <= 100),这个复杂度是可以接受的。 - 空间复杂度 :
O(1)。除了输入网格,只使用了常数级别的额外空间。
关键点与常见错误
- 边界检查 :在尝试访问
grid[i][j+step]或grid[i+step][j]之前,必须确保索引j+step或i+step没有超出矩阵范围。代码中通过if j + K - 1 >= C:和if i + K - 1 >= R:提前判断,避免索引错误。 - K=1 的特殊处理 :这是本题最容易出错的地方。必须理解当
K=1时,横向统计和纵向统计会得到相同的计数(都等于 '.' 的个数),而题目要求不重复计数,因此只能取其中一个,或者直接计算 '.' 的数量。 - 统计所有起始位置 :题目要求统计所有可能的连续段。例如,一行中有连续 3 个 '.',当
K=2时,以第一个 '.' 开头的连续段和以第二个 '.' 开头的连续段是两个不同的段,都需要被统计。上述代码通过遍历每个 '.' 单元格作为起点,自然涵盖了所有起始位置。 - 避免重复的逻辑 :对于
K>1,一个横向段和一个纵向段除非完全重合(这要求段是单个单元格,即K=1),否则不会重复。因此K>1时直接相加是安全的。K=1时则按上述特殊处理。
此解法思路清晰,直接实现了题目要求,并通过特殊判断 K==1 确保了结果的正确性。