475. 目标值子矩阵的数量 | 青训营X豆包MarsCode 技术训练营 | 豆包MarsCode AI 刷题

76 阅读3分钟

题目

{4286E2F6-94BB-4D56-9063-85DA36CEF724}.png

题目解析

本题要求计算一个矩阵中所有子矩阵的数量,其中子矩阵的元素总和等于给定的目标值 target
子矩阵通过选择矩阵的某个矩形区域定义,我们需要遍历可能的矩阵区域并统计满足条件的子矩阵。


解题思路

由于直接枚举所有可能的子矩阵会导致时间复杂度过高 (O(n^4)),我们可以通过以下两步进行优化:

  1. 将二维问题转化为一维问题

    • 固定矩阵的上下边界,计算对应的列和,形成一个一维数组。
    • 目标变为找到这个一维数组中所有和为 target 的子数组数量。
  2. 利用前缀和和哈希表优化子数组求和问题

    • 通过前缀和快速判断某个区间的子数组和是否等于 target
    • 使用哈希表记录前缀和的出现次数,从而快速计算符合条件的子数组。

实现步骤

  1. 遍历上下边界

    • 枚举矩阵的顶部边界 top 和底部边界 bottom
    • 固定 topbottom 后,计算列和数组 colSums
  2. 利用前缀和优化子数组求和问题

    • 遍历列和数组,计算当前的前缀和 prefixSum
    • 检查哈希表中是否存在 prefixSum - target,如果存在,则说明当前前缀和中存在一个区间满足条件。
    • 更新哈希表中当前前缀和的出现次数。
  3. 累加计数

    • 对于每对上下边界,累加所有满足条件的子矩阵数量。

代码实现

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;

int solution(vector<vector<int>>& matrix, int target) {
    int rows = matrix.size();
    int cols = matrix[0].size();
    int count = 0;

    // 遍历每对可能的上下边界
    for (int top = 0; top < rows; ++top) {
        vector<int> colSums(cols, 0); // 当前上下边界的列和
        
        for (int bottom = top; bottom < rows; ++bottom) {
            // 更新列和
            for (int col = 0; col < cols; ++col) {
                colSums[col] += matrix[bottom][col];
            }

            // 利用前缀和和哈希表计算满足条件的子矩阵数量
            unordered_map<int, int> prefixSumCount;
            prefixSumCount[0] = 1; // 初始前缀和为0
            int prefixSum = 0;

            for (int col = 0; col < cols; ++col) {
                prefixSum += colSums[col];
                if (prefixSumCount.find(prefixSum - target) != prefixSumCount.end()) {
                    count += prefixSumCount[prefixSum - target];
                }
                prefixSumCount[prefixSum]++;
            }
        }
    }
    
    return count;
}

int main() {
    vector<vector<int>> v1 = {{-1, 1, 0}, {1, 1, 1}, {0, 1, 0}};
    vector<vector<int>> v2 = {{-1, -1}, {-1, 1}};
    vector<vector<int>> v3 = {{-1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

    cout << (solution(v1, 0) == 7) << endl;
    cout << (solution(v2, 0) == 2) << endl;
    cout << (solution(v3, 10) == 2) << endl;

    return 0;
}

示例讲解

  1. 输入矩阵 v1 = [[-1, 1, 0], [1, 1, 1], [0, 1, 0]],目标值 target = 0

    • 固定 top = 0bottom = 0 时,计算 colSums 后求和为目标值的子数组数量。
    • 固定 top = 0bottom = 1 时,重复上述操作。
    • 累计总和,最终返回结果为 7
  2. 输入矩阵 v2 = [[-1, -1], [-1, 1]],目标值 target = 0

    • 类似方法统计满足条件的子矩阵,结果为 2
  3. 输入矩阵 v3 = [[-1, 2, 3], [4, 5, 6], [7, 8, 9]],目标值 target = 10

    • 累计统计结果为 2

复杂度分析

  1. 时间复杂度:

    • 外层两层循环枚举上下边界,时间复杂度为 O(rows^2)
    • 内层利用哈希表计算子数组数量,时间复杂度为 O(cols)
    • 总时间复杂度为 O(rows^2 X cols)
  2. 空间复杂度:

    • 使用了额外的 colSums 数组和 prefixSumCount 哈希表,空间复杂度为 (O(\text{cols}))。

总结

本题通过转化为一维问题,结合前缀和和哈希表优化,实现了高效计算满足条件的子矩阵数量的目标。