题目描述
小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
解题思路
前缀和矩阵:我们可以先计算一个前缀和矩阵,其中每个元素 prefix_sum[i][j] 表示从矩阵的左上角 (0, 0) 到 (i, j) 的子矩阵的和。
子矩阵和计算:利用前缀和矩阵,我们可以快速计算任意子矩阵的和。假设我们需要计算从 (x1, y1) 到 (x2, y2) 的子矩阵的和,可以通过以下公式计算: sum = prefix_sum[x2][y2] - prefix_sum[x1-1][y2] - prefix_sum[x2][y1-1] + prefix_sum[x1-1][y1-1]
遍历所有子矩阵:我们需要遍历所有可能的子矩阵,并计算它们的和,检查是否等于目标值。
代码实现
def solution(matrix: list, target: int) -> int:
# write code here
rows = len(matrix)
cols = len(matrix[0]) if rows > 0 else 0
# 计算前缀和矩阵
prefix_sum = [[0] * (cols + 1) for _ in range(rows + 1)]
for i in range(1, rows + 1):
for j in range(1, cols + 1):
prefix_sum[i][j] = matrix[i-1][j-1] + prefix_sum[i-1][j] + prefix_sum[i][j-1] - prefix_sum[i-1][j-1]
count = 0
# 遍历所有可能的子矩阵
for x1 in range(1, rows + 1):
for y1 in range(1, cols + 1):
for x2 in range(x1, rows + 1):
for y2 in range(y1, cols + 1):
# 计算子矩阵的和
submatrix_sum = prefix_sum[x2][y2] - prefix_sum[x1-1][y2] - prefix_sum[x2][y1-1] + prefix_sum[x1-1][y1-1]
# 检查是否等于目标值
if submatrix_sum == target:
count += 1
return count
return 0
if __name__ == '__main__':
print(solution(matrix=[[-1, 1, 0], [1, 1, 1], [0, 1, 0]], target=0) == 7)
print(solution(matrix=[[-1, -1], [-1, 1]], target=0) == 2)
print(solution(matrix=[[-1, 2, 3], [4, 5, 6], [7, 8, 9]], target=10) == 2)
关键步骤
计算前缀和矩阵:这部分代码用于计算从 (0, 0) 到 (i, j) 的子矩阵的和。
遍历所有子矩阵:这部分代码用于遍历所有可能的子矩阵,并计算它们的和,检查是否等于目标值。
可能出现的问题 : 如何优化子矩阵求和的时间复杂度?
优化子矩阵求和的时间复杂度可以通过减少不必要的计算来实现。当前的代码使用了四重循环来遍历所有可能的子矩阵,这导致了时间复杂度为 O(n^4),其中 n 是矩阵的边长。我们可以通过使用哈希表来优化这个过程,将时间复杂度降低到 O(n^3)。
优化思路
前缀和矩阵:我们仍然需要计算前缀和矩阵,以便快速计算任意子矩阵的和。
哈希表优化:我们可以使用哈希表来记录每一行的前缀和,并在遍历过程中检查是否存在满足条件的子矩阵。
优化后的代码框架
def solution(matrix: list, target: int) -> int:
rows = len(matrix)
cols = len(matrix[0]) if rows > 0 else 0
# 计算前缀和矩阵
prefix_sum = [[0] * (cols + 1) for _ in range(rows + 1)]
for i in range(1, rows + 1):
for j in range(1, cols + 1):
prefix_sum[i][j] = matrix[i-1][j-1] + prefix_sum[i-1][j] + prefix_sum[i][j-1] - prefix_sum[i-1][j-1]
count = 0
# 遍历所有可能的子矩阵
for x1 in range(1, rows + 1):
for x2 in range(x1, rows + 1):
# 使用哈希表记录每一行的前缀和
prefix_sum_map = {0: 1} # 初始化哈希表,包含一个和为0的子矩阵
for y in range(1, cols + 1):
# 计算当前行的前缀和
current_sum = prefix_sum[x2][y] - prefix_sum[x1-1][y]
# 检查是否存在满足条件的子矩阵
if current_sum - target in prefix_sum_map:
count += prefix_sum_map[current_sum - target]
# 更新哈希表
if current_sum in prefix_sum_map:
prefix_sum_map[current_sum] += 1
else:
prefix_sum_map[current_sum] = 1
return count
if __name__ == '__main__':
print(solution(matrix=[[-1, 1, 0], [1, 1, 1], [0, 1, 0]], target=0) == 7)
print(solution(matrix=[[-1, -1], [-1, 1]], target=0) == 2)
print(solution(matrix=[[-1, 2, 3], [4, 5, 6], [7, 8, 9]], target=10) == 2)
关键步骤
计算前缀和矩阵:这部分代码用于计算从 (0, 0) 到 (i, j) 的子矩阵的和。
哈希表优化:在遍历过程中,我们使用哈希表记录每一行的前缀和,并在每次计算当前行的前缀和时,检查是否存在满足条件的子矩阵。
时间复杂度 通过使用哈希表,我们将时间复杂度从 O(n^4) 降低到 O(n^3),其中 n 是矩阵的边长。
总结
本题考查了前缀和矩阵,并给出了优化思路。