问题描述
小M最近在研究矩阵,他对矩阵中的子矩阵很感兴趣,给定一个矩阵matrix和一个目标值 target,他的任务是找到所有总和等于目标值的非空子矩阵的数量 子矩阵通过选择矩阵的某个矩形区域定义,形式为(x1,y1,x2,y2),其中(x1,y1)表示左上角的坐标,(x2,y2)表示右下角的坐标。一个子短阵包含短阵中所 有位于这个矩形区域内的单元格。如果两个子矩阵的坐标不同(如 x1 != x1'或 y1!= y1'),则这两个子矩阵被认为是不同的。 你需要返回满足条件的子矩阵数量。
题目分析
题目内容
- 给定一个矩阵 matrix 和一个目标值 target,我们需要找到矩阵中所有总和等于目标值的 非空子矩阵 的数量。
- 子矩阵 是通过选择矩阵的某个矩形区域定义的,可以表示为 (x1, y1, x2, y2),其中 (x1, y1) 是左上角坐标,(x2, y2) 是右下角坐标。
- 不同的坐标定义的子矩阵被认为是不同的,即使它们的内容相同。
约束
- 题目没有明确说明矩阵的大小范围,但复杂度取决于矩阵大小,需注意优化算法性能。
- 矩阵中的元素可以是负数、正数或零。
解题思路
暴力解法
- 暴力解法通过枚举所有可能的子矩阵坐标 (x1, y1, x2, y2)。
- 然后计算该子矩阵的元素总和,判断是否等于目标值。
- 如果相等,则将计数器加一。
问题难点
- 如何高效计算子矩阵的和?
- 暴力解法在每次枚举子矩阵时需要重新计算子矩阵内的元素总和,其时间复杂度较高。
- 如何减少枚举的重复计算?
- 利用前缀和技巧,快速得到任意子矩阵的和。
复杂度分析
- 暴力解法需要:
- 枚举所有的子矩阵,其枚举的复杂度是 𝑂((𝑟𝑜𝑤𝑠⋅𝑐𝑜𝑙𝑠)2)。
- 每次计算子矩阵的和需要遍历子矩阵内的所有元素,复杂度为𝑂(𝑟𝑜𝑤𝑠⋅𝑐𝑜𝑙𝑠)。
- 总体时间复杂度是𝑂((𝑟𝑜𝑤𝑠⋅𝑐𝑜𝑙𝑠)3),对于较大矩阵不可接受。
- 优化解法可以通过减少重复计算,将复杂度降至𝑂(𝑟𝑜𝑤𝑠2⋅𝑐𝑜𝑙𝑠)或更低。
代码思路
暴力解法(当前代码实现)
外层循环:
- 使用四重嵌套循环,枚举所有可能的子矩阵左上角 (x1, y1) 和右下角 (x2, y2)。 内层计算:
- 在确定子矩阵的边界后,通过遍历子矩阵的所有单元格,计算子矩阵的总和。
- 如果总和等于目标值 target,则将计数器 count 加一。 返回结果:
- 返回统计的子矩阵数量。
代码分析
代码实现
def solution(matrix: list, target: int) -> int:
rows = len(matrix) # 获取矩阵的行数
cols = len(matrix[0]) # 获取矩阵的列数
count = 0 # 用于计数满足条件的子矩阵数量
# 枚举子矩阵左上角坐标
for x1 in range(rows):
for y1 in range(cols):
# 枚举子矩阵右下角坐标
for x2 in range(x1, rows):
for y2 in range(y1, cols):
submatrix_sum = 0 # 初始化子矩阵的和
# 遍历子矩阵内的所有元素
for i in range(x1, x2 + 1):
for j in range(y1, y2 + 1):
submatrix_sum += matrix[i][j] # 累加子矩阵元素
# 判断子矩阵和是否等于目标值
if submatrix_sum == target:
count += 1 # 满足条件时计数加一
return count
- rows 和 cols 用于获取矩阵的维度。
- 第一层与第二层循环枚举子矩阵的左上角 (x1, y1)。
- 第三层与第四层循环枚举子矩阵的右下角 (x2, y2)。
- 内部通过两层循环,计算当前子矩阵的元素总和。
- 如果当前子矩阵总和等于目标值,则将计数器增加。
总结
优点
- 代码逻辑清晰,易于理解。
- 暴力枚举所有可能的子矩阵,能够保证找到所有满足条件的解。
缺点
- 效率低下:在大矩阵情况下性能较差,可能会导致超时问题。
- 可优化方向:
- 利用 前缀和 降低计算子矩阵总和的时间复杂度。
- 枚举时优化子矩阵边界选择,避免重复计算。
改进建议
可以通过引入前缀和技巧优化解法,将复杂度降为𝑂(𝑟𝑜𝑤𝑠2⋅𝑐𝑜𝑙𝑠):
- 固定行边界:通过累加行的方式将二维问题转化为一维问题。
- 快速求子数组和:在一维问题中使用哈希表快速找到满足条件的子数组数量。 优化版本可以进一步提升性能,适用于更大规模的矩阵输入。