leetcode
leetcode 1001 ~ 1050
元素和为目标值的子矩阵数量

元素和为目标值的子矩阵数量

难度:

标签:

题目描述

给出矩阵 matrix 和目标值 target,返回元素总和等于目标值的非空子矩阵的数量。

子矩阵 x1, y1, x2, y2 是满足 x1 <= x <= x2 且 y1 <= y <= y2 的所有单元 matrix[x][y] 的集合。

如果 (x1, y1, x2, y2) 和 (x1', y1', x2', y2') 两个子矩阵中部分坐标不同(如:x1 != x1'),那么这两个子矩阵也不同。

 

示例 1:

输入:matrix = [[0,1,0],[1,1,1],[0,1,0]], target = 0
输出:4
解释:四个只含 0 的 1x1 子矩阵。

示例 2:

输入:matrix = [[1,-1],[-1,1]], target = 0
输出:5
解释:两个 1x2 子矩阵,加上两个 2x1 子矩阵,再加上一个 2x2 子矩阵。

示例 3:

输入:matrix = [[904]], target = 0
输出:0

 

提示:

  • 1 <= matrix.length <= 100
  • 1 <= matrix[0].length <= 100
  • -1000 <= matrix[i][j] <= 1000
  • -10^8 <= target <= 10^8

代码结果

运行时间: 331 ms, 内存: 16.9 MB


/*
 * Approach using Java Streams:
 * 1. Iterate over all possible pairs of rows.
 * 2. For each pair of rows, compute the sum of elements between the rows for each column.
 * 3. Use a HashMap to store the prefix sums and count the number of submatrices that sum up to the target.
 */
import java.util.HashMap;
import java.util.stream.IntStream;

public class SubmatrixSumStream {
    public int numSubmatrixSumTarget(int[][] matrix, int target) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        int result = 0;

        for (int r1 = 0; r1 < rows; r1++) {
            int[] colSum = new int[cols];
            for (int r2 = r1; r2 < rows; r2++) {
                IntStream.range(0, cols).forEach(c -> colSum[c] += matrix[r2][c]);
                result += countSubarraysWithSum(colSum, target);
            }
        }

        return result;
    }

    private int countSubarraysWithSum(int[] arr, int target) {
        HashMap<Integer, Integer> prefixSumMap = new HashMap<>();
        prefixSumMap.put(0, 1);
        int sum = 0;
        int count = 0;

        for (int num : arr) {
            sum += num;
            if (prefixSumMap.containsKey(sum - target)) {
                count += prefixSumMap.get(sum - target);
            }
            prefixSumMap.put(sum, prefixSumMap.getOrDefault(sum, 0) + 1);
        }

        return count;
    }
}

解释

方法:

此题解采用了前缀和与哈希表的方法来计算子矩阵的和。首先,固定子矩阵的上边界top和下边界bottom,对于每个可能的上下边界对,计算从第0列到当前列的子矩阵的和。通过维护一个行前缀和数组rowPrefix来记录当前列的累加和。然后,使用一个哈希表colPrefix_dict来存储所有可能的列前缀和的出现次数,从而快速检查当前列前缀和减去目标值target是否已经出现过,以判断是否存在符合条件的子矩阵。这种方法避免了直接计算每个子矩阵的和,从而提高了效率。

时间复杂度:

O(m^2 * n)

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中提到使用前缀和与哈希表的方法,能否详细解释为什么这种组合效果良好?
前缀和与哈希表的组合在处理子数组或子矩阵的和问题中效果良好,原因在于这种方法能够有效减少重复计算并快速查询。具体来说,前缀和可以用来计算任何子数组或子矩阵的和,通过存储累加值,可以在O(1)时间内得到任意区间的和。而哈希表的使用则允许我们在O(1)的时间复杂度内查找之前出现过的前缀和,这对于快速检查当前前缀和与目标值的差值是否已存在至关重要。在本题中,我们利用哈希表记录所有可能的列前缀和的出现次数,当遍历到新的列时,可以立即判断以当前列为右边界的、符合目标和的子矩阵的数量。这种方法显著提高了效率,尤其是在处理大数据量时。
🦆
哈希表中初始化为`{0: 1}`表示的空矩阵情况是如何帮助统计子矩阵数量的?
在使用哈希表记录前缀和的时候,初始化为`{0: 1}`是为了处理那些从矩阵的起始位置开始且和恰好为目标值的子矩阵。这个初始化意味着存在一个虚拟的前缀和为0,这有助于处理当整个子矩阵(从起始累积到当前位置)的和刚好等于目标值的情况。例如,如果当前的列前缀和正好等于目标值,那么根据前缀和的性质,当前列前缀和减去0(即前缀和本身)会留下一个满足条件的子矩阵,因此我们可以通过检查哈希表来直接统计这类情况的子矩阵数量。
🦆
在计算行前缀和时,为什么需要遍历从top到bottom的每一行?直接计算整个矩阵的行前缀和是否可行?
在计算子矩阵的和时,我们需要考虑各种不同的上边界和下边界的组合,这意味着每一对上下边界定义了一个新的子矩阵范围。为了正确计算每个这样的子矩阵的和,我们需要从当前的上边界top到下边界bottom逐行累加,从而更新行前缀和数组。如果直接计算整个矩阵的行前缀和,我们将无法得到仅限于特定上下边界之间的行的累计和,这是因为不同的上下边界对定义了不同的子矩阵。因此,为了保证精确计算每个可能的子矩阵的和,我们需要根据当前考虑的上下边界动态计算行前缀和。

相关问题