构建直方图
我们用一个 height 数组来表示直方图,其中 height[j] 表示矩阵在第 j 列连续 1 的高度。
当遇到 mat[i][j] = 1 时,height[j] 在上一行的基础上加 1;否则就重置为 0。
// row one
mat[0] = [1,0,1]
height = [1,0,1]
// row two
mat[1] = [1,1,0]
height = [2,1,0]
// row three
mat[2] = [1,1,0]
height = [3,2,0]
可以看到,height 是逐行累积出来的,表示“以当前行为底,每一列的高度”。
例如row = 2的时候,height = [3,2,0],此时在第3行第一列上面1的高度就是3
统计矩阵数量
在有了 height 数组之后,我们只需要计算:以第 i 行为底、以第 j 列为右边界,可以形成多少个全 1 子矩阵。
var numSubmat = function (mat) {
let row = mat.length,
col = mat[0].length;
let height = new Array(col).fill(0);
let res = 0;
for (let i = 0; i < row; ++i) {
for (let j = 0; j < col; ++j) {
height[j] = mat[i][j] === 0 ? 0 : height[j] + 1;
}
for (let j = 0; j < col; ++j) {
let minHeight = height[j];
for (let k = j; k >= 0 && minHeight > 0; --k) {
minHeight = Math.min(minHeight, height[k]);
res += minHeight;
}
}
}
return res;
};
内层循环的含义
关键部分在于第二层循环:
- 外层
j表示“以第 j 列为右边界”。 - 内层
k从j向左扫描,表示“矩形的左边界”。 minHeight表示区间[k, j]内的最小高度,也就是矩形的可用高度。
举个例子:
height = [3,2,0],假设 j = 1(第二列,高度=2)
- 当
k = 1时,只考虑单独第 2 列,高度=2,可以形成两个矩形。 - 当
k = 0时,考虑区间[0,1],最小高度 = min(3,2) = 2,可以形成两个更宽的矩形。
所以,以第 1 列为右边界的矩阵数 = 2(单列) + 2(两列) = 4。