力扣3654:二维矩阵连续空位统计

力扣第 3654 题《First Step》是一个典型的二维矩阵遍历与统计问题,核心目标是在一个 R×C 的矩阵中,统计所有长度为 K 的连续空位(用 '.' 表示)的数量,统计方向包括横向(从左到右)纵向(从上到下) ,但同一段连续空位不应在横向和纵向被重复计数

问题解构

  1. 输入
    • R:矩阵行数。
    • C:矩阵列数。
    • K:需要统计的连续空位长度。
    • 一个 R×C 的字符矩阵,包含 '.'(空位)和 '#'(障碍物)。
  2. 输出
    • 一个整数,表示所有长度为 K 的连续空位段的数量。
  3. 关键约束
    • K 可能为 1。当 K=1 时,每个 '.' 单元格自身就是一个长度为 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 增加时,窗口滑动,可以利用之前的信息,但本题数据规模通常允许简单的逐段检查。
  2. 纵向统计

    • 逻辑与横向完全对称,但遍历顺序变为先列后行。
    • 遍历每一列 j 0 <= j < C)。
    • 对于该列的每一行 i 0 <= i < R),如果 grid[i][j] 是 '.',则尝试向下扩展。
    • i 开始,连续检查 K 个单元格(grid[i][j]grid[i+K-1][j])。
    • 如果这 K 个单元格都在矩阵范围内且都是 '.',则找到一个有效的纵向连续段,计数器加一。
  3. 处理 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)。除了输入网格,只使用了常数级别的额外空间。

关键点与常见错误

  1. 边界检查 :在尝试访问 grid[i][j+step]grid[i+step][j] 之前,必须确保索引 j+stepi+step 没有超出矩阵范围。代码中通过 if j + K - 1 >= C:if i + K - 1 >= R: 提前判断,避免索引错误。
  2. K=1 的特殊处理 :这是本题最容易出错的地方。必须理解当 K=1 时,横向统计和纵向统计会得到相同的计数(都等于 '.' 的个数),而题目要求不重复计数,因此只能取其中一个,或者直接计算 '.' 的数量。
  3. 统计所有起始位置 :题目要求统计所有可能的连续段。例如,一行中有连续 3 个 '.',当 K=2 时,以第一个 '.' 开头的连续段和以第二个 '.' 开头的连续段是两个不同的段,都需要被统计。上述代码通过遍历每个 '.' 单元格作为起点,自然涵盖了所有起始位置。
  4. 避免重复的逻辑 :对于 K>1,一个横向段和一个纵向段除非完全重合(这要求段是单个单元格,即 K=1),否则不会重复。因此 K>1 时直接相加是安全的。K=1 时则按上述特殊处理。

此解法思路清晰,直接实现了题目要求,并通过特殊判断 K==1 确保了结果的正确性。


参考来源

相关推荐
basketball6161 小时前
并查集基础算法总结 C++ 实现
开发语言·c++·算法
满昕欢喜1 小时前
SQL Server的概述与安装
数据库·sqlserver
2501_930707782 小时前
使用C#代码在 Excel 中为数据透视表添加筛选器
数据库·数据挖掘·数据分析
故事和你912 小时前
洛谷-【图论2-2】最短路3
开发语言·数据结构·c++·算法·动态规划·图论
yong99902 小时前
基于VC++的图像匹配金字塔算法
c++·算法·计算机视觉
Rhi6372 小时前
第 4 篇:用JWT与角色权限构筑安全的API防线
算法
TDengine (老段)2 小时前
TDengine 数据库创建与参数详解
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
fengfuyao9852 小时前
基于MATLAB的ALOHA防碰撞、二进制搜索算法和帧时隙算法
人工智能·算法·matlab
IT策士2 小时前
Django 从 0 到 1 打造完整电商平台:电商项目需求分析与数据库设计
数据库·django·需求分析