AI刷题-目标子矩阵的数量 | 青训营笔记

117 阅读4分钟

问题描述

小M最近在研究矩阵,他对矩阵中的子矩阵很感兴趣。给定一个矩阵 matrix 和一个目标值 target,他的任务是找到所有总和等于目标值的非空子矩阵的数量。

子矩阵通过选择矩阵的某个矩形区域定义,形式为 (x1, y1, x2, y2),其中 (x1, y1) 表示左上角的坐标,(x2, y2) 表示右下角的坐标。一个子矩阵包含矩阵中所有位于这个矩形区域内的单元格。如果两个子矩阵的坐标不同(如 x1 != x1' 或 y1 != y1'),则这两个子矩阵被认为是不同的。

你需要返回满足条件的子矩阵数量。


测试样例

样例1:

输入:matrix = [[-1,1,0], [1,1,1], [0,1,0]] ,target = 0
输出:7

样例2:

输入:matrix = [[-1,-1], [-1,1]] ,target = 0
输出:2

样例3:

输入:matrix = [[-1,2,3], [4,5,6], [7,8,9]] ,target = 10
输出:2

问题分析

给定一个矩阵 matrix 和一个目标值 target,我们需要找出所有满足条件的子矩阵数量。子矩阵的总和等于目标值 target。问题的关键在于如何高效地遍历所有可能的子矩阵,且求得其总和,并与目标值 target 进行比较。

子矩阵定义

  • 子矩阵由矩阵中的一个矩形区域定义,通常是由 (x1, y1)(x2, y2) 的区域,其中 (x1, y1) 是左上角坐标,(x2, y2) 是右下角坐标。
  • 目标是找出所有这些矩形区域的和为 target 的子矩阵。

思路

1. 暴力方法:

暴力方法是遍历矩阵的所有子矩阵,计算其和并与目标值比较。我们可以通过枚举所有的矩形区域,并逐一计算每个子矩阵的和。然而,直接的暴力方法会导致时间复杂度非常高,具体是 O(n^4)(假设矩阵大小为 n × n),因为我们需要枚举所有的矩形区域并计算其和。

2. 优化:

我们可以利用 前缀和 的技巧来优化子矩阵和的计算。前缀和可以帮助我们快速计算任意子矩阵的和,从而避免对每个子矩阵进行重复计算。

3. 核心优化思路:

我们将问题转化为二维数组的“一维问题”。具体步骤如下:

  1. 前缀和的使用:

    • 首先,定义一个一维的前缀和数组 prefix,其中 prefix[j] 表示从矩阵的第一列到当前列的元素的和。通过前缀和可以快速计算任意矩形区域的和。
  2. 求子矩阵的和:

    • 对于每一对行 ij,我们可以通过固定行的范围(即 ij),将问题转化为一维问题。然后,对于每列,我们只需计算该列区间的总和,再通过滑动窗口或哈希表等方法,找出和为 target 的子矩阵。

4. 具体步骤:

  1. 遍历矩阵的每一对行 (i, j)
  2. 对于每一对行,计算该行之间每一列的和,并将其存入一维数组。
  3. 在这个一维数组上,问题就转化为“寻找和为 target 的连续子数组”的问题。使用哈希表来记录累计和的频率,快速计算和为目标值的子数组个数。

代码实现

python
Copy code
from collections import defaultdict

def numSubmatrixSumTarget(matrix, target):
    m, n = len(matrix), len(matrix[0])
    result = 0
    
    # 遍历每对行 (i, j)
    for i in range(m):
        # 用于存储每一列的和
        col_sum = [0] * n
        # 遍历每一对行
        for j in range(i, m):
            # 更新每一列的和
            for k in range(n):
                col_sum[k] += matrix[j][k]
            
            # 使用哈希表来查找满足条件的子数组
            sum_count = defaultdict(int)
            sum_count[0] = 1  # 对应子矩阵和为0的情况
            current_sum = 0
            # 遍历当前列和的前缀和
            for s in col_sum:
                current_sum += s
                result += sum_count[current_sum - target]
                sum_count[current_sum] += 1
    
    return result

代码说明:

  1. 外层循环:我们遍历每一对行 (i, j),表示当前考虑的矩形区域的上下边界。
  2. col_sum 数组:表示当前行区间内每列的累积和。对于每一对行 (i, j),我们将从行 i 到行 j 的矩阵区域在列方向上的和累计到 col_sum 中。
  3. 哈希表 sum_count:我们使用一个哈希表来存储每个前缀和出现的次数。对于每一列的和,计算当前前缀和 current_sum,然后查找是否存在前缀和 current_sum - target,如果存在,则表示有一个子矩阵的和等于 target
  4. current_sum:用于跟踪当前行区间的和。

复杂度分析:

  • 时间复杂度:外层两层循环遍历所有行对 (i, j),内层遍历列。因此时间复杂度为 O(m^2 * n),其中 m 是行数,n 是列数。
  • 空间复杂度:哈希表 sum_count 存储前缀和,空间复杂度是 O(n)。