思路
朴素做法首先枚举矩形左上角和右下角坐标,复杂度为$O(n^2) * O(n^2)$,对于每个方案,判断内部是否全为1也是$O(n^2)$的。因此总的复杂度爆炸$O(n^6)$。
当然,判断内部1这一步可以用前缀和来优化掉,但是复杂度依旧是比较恐怖的。
这一题借鉴了LC-84的思路,站在巨人的肩膀上思考。已知可以线性时间内求一堆柱子中的最大面积,如何应用到这个问题中来?
可以考虑按行把大矩阵切割成n份,高度从1到n,对于每一个子矩阵,在切割线上方都会有若干个连续的1,这些连续的1可以看成是柱子。
如何求某个位置上方有几个连续的1呢?这个可以通过递推来求。不妨设某个点的下标为(i, j)
,用f[i,j]
表示这个点上方有多少个连续的1。我们从上到下递推,如果当前位置的值为0,那么f[i,j] = 0
;如果值为1,那么f[i,j] = f[i - 1,j] + 1
。因此可以$O(n^2)$时间内预处理出每个点上方有多少连续的1。
之后按行枚举整个矩阵,每一行都变成了一个LC-84的模板题。因此总的时间复杂度也是$O(n^2)$的。
代码
class Solution {
public:
int maxRectangleArea(vector<int>& h) {
int n = h.size();
stack<int> stk;
vector<int> left(n), right(n);
for(int i = 0; i < n; i++) {
while(stk.size() && h[stk.top()] >= h[i]) stk.pop();
if(stk.empty()) left[i] = -1;
else left[i] = stk.top();
stk.push(i);
}
stk = stack<int>();
for(int i = n - 1; i >= 0; i--) {
while(stk.size() && h[stk.top()] >= h[i]) stk.pop();
if(stk.empty()) right[i] = n;
else right[i] = stk.top();
stk.push(i);
}
int res = 0;
for(int i = 0; i < n; i++) {
res = max(res, h[i] * (right[i] - left[i] - 1));
}
return res;
}
int maximalRectangle(vector<vector<char>>& matrix) {
if(matrix.empty() || matrix[0].empty()) return 0;
int n = matrix.size(), m = matrix[0].size();
vector<vector<int>> h(n, vector<int>(m));
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(matrix[i][j] == '1'){
if(i) h[i][j] = h[i - 1][j] + 1;
else h[i][j] = 1;
}
}
}
int res = 0;
for(int i = 0; i < n; i++)
res = max(res, maxRectangleArea(h[i]));
return res;
}
};
拓展
求最大正方形?LC-221
思路
可以考虑DP。f[i,j]
表示以[i,j]
为右下角的最大正方形的边长。如果matrix[i,j] == 1
那么f[i,j] = min(f[i - 1][j], f[i][j - 1], f[i - 1][j - 1]) + 1
; 否则f[i,j] = 0
。
解释一下递推式: 已知右下角的数为1,那么最大的正方形的大小受限于以matrix[i - 1][j]
,matrix[i][j - 1]
,matrix[i - 1][j - 1]
为右下角的最大正方形的边长。举个例子:
1 1 1 1
0 1 1 1
0 1 1 1
0 1 1 1
(假设下标从1开始)f[4,4] = min(f[3,3], f[4,3], f[3,4]) + 1 = min(2, 3, 2) + 1 = 3
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
f[4,4] = min(f[3,3], f[4,3], f[3,4]) + 1 = min(3, 3, 3) + 1 = 4
求出f数组后遍历一下找最大即可。
时间复杂度$O(n^2)$
代码
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int n = matrix.size(), m = matrix[0].size();
vector<vector<int>> f(n + 1, vector<int>(m + 1));
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(matrix[i - 1][j - 1] == '1')
f[i][j] = min(f[i - 1][j], min(f[i][j - 1], f[i - 1][j - 1])) + 1;
}
}
int res = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
res = max(res, f[i][j]);
return res * res;
}
};
- Post link: https://scnujackychen.github.io/2021/03/11/LC-85/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub IssuesGitHub Discussions