Given a binary matrix matrix
of size m x n
, where each element is either 0 or 1, your task is to count the number of submatrices that contain only 1s. A submatrix is any contiguous rectangular area within the matrix, and all its elements must be 1s for it to be counted. The elements of the matrix cannot be reused across different submatrices, but overlapping is allowed as long as each counted submatrix is a distinct rectangle of 1s.
matrix
(a list of lists, each containing 0 or 1)1 <= m, n <= 150
; Each element is either 0 or 1The naive way to solve this problem is to check every possible submatrix and count those that consist only of 1s. However, this approach is highly inefficient for larger matrices, as the number of possible submatrices grows rapidly with the matrix size.
To optimize, we need to avoid redundant work. Instead of checking every possible rectangle, we can use dynamic programming or a histogram-based approach to efficiently count the valid submatrices. The key insight is that for each cell, we can determine how many submatrices end at that cell by considering the number of consecutive 1s above it and to its left.
The process involves:
We use a dynamic programming and histogram approach to efficiently count submatrices with all ones:
matrix[i][j]
, compute heights[i][j]
as the number of consecutive 1s in the column above (including itself).matrix[i][j] == 0
, set heights[i][j] = 0
.heights[i][j]
as the height of a histogram bar.This approach avoids redundant checks and leverages previously computed values, making it much more efficient than brute force.
Consider the following input matrix:
matrix = [ [1, 0, 1], [1, 1, 0], [1, 1, 0] ]
So, the answer is 13.
Brute-force approach:
The optimized approach is much faster and feasible for the largest allowed input sizes.
By transforming each row into a histogram of consecutive 1s and efficiently counting the number of submatrices ending at each cell, we can solve the "Count Submatrices With All Ones" problem in a much more efficient way than brute force. The key insight is to use dynamic programming to build up solutions from previous rows and columns, leveraging the structure of the matrix to avoid redundant work. This approach is elegant, scalable, and highlights the power of reusing computations in 2D problems.
class Solution:
def numSubmat(self, matrix):
m, n = len(matrix), len(matrix[0])
heights = [[0]*n for _ in range(m)]
for i in range(m):
for j in range(n):
if matrix[i][j]:
heights[i][j] = heights[i-1][j] + 1 if i > 0 else 1
res = 0
for i in range(m):
for j in range(n):
min_h = heights[i][j]
for k in range(j, -1, -1):
if heights[i][k] == 0:
break
min_h = min(min_h, heights[i][k])
res += min_h
return res
class Solution {
public:
int numSubmat(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> heights(m, vector<int>(n, 0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i][j]) {
heights[i][j] = (i > 0 ? heights[i-1][j] : 0) + 1;
}
}
}
int res = 0;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
int min_h = heights[i][j];
for (int k = j; k >= 0 && heights[i][k] > 0; --k) {
min_h = min(min_h, heights[i][k]);
res += min_h;
}
}
}
return res;
}
};
class Solution {
public int numSubmat(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
int[][] heights = new int[m][n];
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i][j] == 1) {
heights[i][j] = (i > 0 ? heights[i-1][j] : 0) + 1;
}
}
}
int res = 0;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
int min_h = heights[i][j];
for (int k = j; k >= 0 && heights[i][k] > 0; --k) {
min_h = Math.min(min_h, heights[i][k]);
res += min_h;
}
}
}
return res;
}
}
var numSubmat = function(matrix) {
const m = matrix.length, n = matrix[0].length;
const heights = Array.from({length: m}, () => Array(n).fill(0));
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (matrix[i][j]) {
heights[i][j] = (i > 0 ? heights[i-1][j] : 0) + 1;
}
}
}
let res = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
let min_h = heights[i][j];
for (let k = j; k >= 0 && heights[i][k] > 0; --k) {
min_h = Math.min(min_h, heights[i][k]);
res += min_h;
}
}
}
return res;
};