- 枚举+前缀和
关键是如何枚举出所有可能的矩形,并且尽可能高效。
对于每个矩形而言,用四个变量可以去唯一确定。i,j,k,now
,含义分别是,这个矩形头部的列序号、尾部的列序号、头部的行号、一共多少行。
其中 直接两层for循环即可,对于k、now
,可以这样处理,从第一行一直遍历到最后一行,记录下能够构成矩形的一行的行的个数,然后用它去更新答案。
为了快速判定某一行的某一段是不是全都是1,可以记录一下每一行的前缀和。
时间复杂度:
class Solution {
public:
int maximalRectangle(vector<vector<char>>& a) {
if(a.size()==0 || a[0].size()==0) return 0;
int ans = 0 ;
int m = a.size() , n = a[0].size();
vector<vector<int>> s(m,vector<int>(n));
for(int i=0;i<m;i++) {
s[i][0] = a[i][0]-'0';
for(int j=1;j<n;j++){
s[i][j] = s[i][j-1] + a[i][j]-'0';
}
}
for(int l=0;l<n;l++){
for(int r=l;r<n;r++){
int now = 0;
for(int k=0;k<m;k++){
int ss = s[k][r];
if(l>0){
ss -= s[k][l-1];
}
if( ss == r-l+1 ){
now++;
}else{
ans = max(ans,(r-l+1)*now);
now = 0;
}
}
if(now){
ans = max(ans,(r-l+1)*now); // 不要忘记
}
}
}
return ans;
}
};
-
单调栈
思路:
把矩阵的每一行作为84题那样的矩形的最底层,然后依次处理即可。
(图片来自力扣)
理解这张图、然后预处理一下矩阵。
时间复杂度:
class Solution {
public:
int maximalRectangle(vector<vector<char>>& a) {
if(a.size()==0 || a[0].size()==0) return 0;
int ans = 0 ;
int m = a.size() , n = a[0].size();
vector<vector<int>> dp(m,vector<int>(n,0));
// 从这一行的这一列开始,往上连续的最多有多少个1.
for(int j=0;j<n;j++){
if(a[0][j]=='1') dp[0][j] = 1;
}
for(int i=1;i<m;i++){
for(int j=0;j<n;j++){
if(a[i][j]=='1'){
dp[i][j] = dp[i-1][j] + 1;
}else{
dp[i][j] = 0;
}
}
}
for(int i=0;i<m;i++){
ans = max(ans,largestRectangleArea(dp[i]));
}
return ans;
}
// LeetCode 84 题的代码,完全可以直接拿来使用
int largestRectangleArea(vector<int>& a) {
typedef pair<int,int> P;
int ans = 0;
stack<P> s;
for(int x:a){
if(s.empty() || s.top().first<=x){
s.push(make_pair(x,1));
}else{
int width = 0;
while(!s.empty() && s.top().first>x){
P p = s.top();
s.pop();
width += p.second;
ans = max(ans,width*p.first);
}
s.push(make_pair(x,width+1));
}
}
int width = 0;
while(!s.empty()){
P p = s.top();
s.pop();
width += p.second;
ans = max(ans,width*p.first);
}
return ans;
}
};
- 下面参考一位网友的写法、换了一种写法去枚举矩形,不过时间复杂度仍为:
思考一下,这种写法是怎么优化到上面的 的,这种做法是枚举每一个矩形的右下角,然后去维护一个单调的轮廓(正是单调栈的思想)。
但是冗余的操作呢?很明显: 其实有太多重复的计算过程。
于是最终可以一行一行的去就算,而不是一个一个地去计算。
class Solution {
public:
int maximalRectangle(vector<vector<char>>& a) {
if(a.size()==0 || a[0].size()==0){
return 0;
}
int m = a.size(), n = a[0].size(), ans = 0;
vector<vector<vector<int>>> dp(m+1,vector<vector<int>>(n+1,vector<int>(2,0)));
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(a[i-1][j-1]=='1'){
dp[i][j][0] = dp[i][j-1][0] + 1; //向左连续的1的个数
dp[i][j][1] = dp[i-1][j][1] + 1; //向上连续的1的个数
int res = 0;
int ls = dp[i][j][0];
int rows = dp[i][j][1];
for(int k=0;k<rows;k++){
ls = min(ls,dp[i-k][j][0]);
ans = max(ans,ls*(k+1));
}
ans = max(ans,res);
}
}
}
return ans;
}
};