问题描述
小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. 核心优化思路:
我们将问题转化为二维数组的“一维问题”。具体步骤如下:
-
前缀和的使用:
- 首先,定义一个一维的前缀和数组
prefix,其中prefix[j]表示从矩阵的第一列到当前列的元素的和。通过前缀和可以快速计算任意矩形区域的和。
- 首先,定义一个一维的前缀和数组
-
求子矩阵的和:
- 对于每一对行
i和j,我们可以通过固定行的范围(即i到j),将问题转化为一维问题。然后,对于每列,我们只需计算该列区间的总和,再通过滑动窗口或哈希表等方法,找出和为target的子矩阵。
- 对于每一对行
4. 具体步骤:
- 遍历矩阵的每一对行
(i, j)。 - 对于每一对行,计算该行之间每一列的和,并将其存入一维数组。
- 在这个一维数组上,问题就转化为“寻找和为
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
代码说明:
- 外层循环:我们遍历每一对行
(i, j),表示当前考虑的矩形区域的上下边界。 col_sum数组:表示当前行区间内每列的累积和。对于每一对行(i, j),我们将从行i到行j的矩阵区域在列方向上的和累计到col_sum中。- 哈希表
sum_count:我们使用一个哈希表来存储每个前缀和出现的次数。对于每一列的和,计算当前前缀和current_sum,然后查找是否存在前缀和current_sum - target,如果存在,则表示有一个子矩阵的和等于target。 current_sum:用于跟踪当前行区间的和。
复杂度分析:
- 时间复杂度:外层两层循环遍历所有行对
(i, j),内层遍历列。因此时间复杂度为 O(m^2 * n),其中m是行数,n是列数。 - 空间复杂度:哈希表
sum_count存储前缀和,空间复杂度是 O(n)。