题目链接:Maximal Rectangle

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

这道题的要求是在0-1矩阵中找出面积最大的全1矩阵。

如果下面的粗略分析看不懂的话,关于这个问题非常具体详细的解析可以参考:https://www.cnblogs.com/lupx/archive/2015/10/20/leetcode-85.html

1. 基于Largest Rectangle in Histogram

假设把矩阵沿着某一行分开,然后把分开的行作为底面,将自底面往上的矩阵看成一个直方图(histogram)。直方图的中每个项的高度就是从底面行开始往上1的数量。根据Largest Rectangle in Histogram就可以求出当前行作为矩阵下边缘的一个最大矩阵。接下来如果对每一行都做一次Largest Rectangle in Histogram,从其中选出最大的矩阵,那么它就是整个矩阵中面积最大的子矩阵。

如何计算某一行为底面时直方图的高度呢?如果重新计算,那么每次需要的计算数量就是当前行数乘以列数。然而会发现一些动态规划的踪迹,如果知道上一行直方图的高度,就只需要看新加进来的行(底面)上对应的列元素是不是0,如果是,则高度是0,否则则是上一行直方图的高度加1。利用历史信息,就可以在线行时间内完成对高度的更新。由于Largest Rectangle in Histogram的算法复杂度是O(n)。所以完成对一行为底边的矩阵求解复杂度是O(n+n)=O(n)。接下来对每一行都做一次,那么算法总时间复杂度是O(m*n)。

时间复杂度:O(mn)

空间复杂度:O(n)

 1 class Solution
 2 {
 3 public:
 4     int maximalRectangle(vector<vector<char> > &matrix)
 5     {
 6         if(matrix.size() == 0 || matrix[0].size() == 0)
 7             return 0;
 8         
 9         int res = 0;
10         vector<int> height(matrix[0].size(), 0);
11         for(int i = 0; i < matrix.size(); ++ i)
12         {
13             for(int j = 0; j < matrix[0].size(); ++ j)
14                 height[j] = matrix[i][j] == '0' ? 0 : height[j] + 1;
15             res = max(res, largestRectangleArea(height));
16         }
17         return res;
18     }
19 private:
20     int largestRectangleArea(vector<int> &height)
21     {
22         int res = 0;
23         stack<int> si;
24         height.push_back(0);
25         for(int i = 0; i < height.size(); ++ i)
26         {
27             while(!si.empty() && height[si.top()] >= height[i])
28             {
29                 int h = height[si.top()];
30                 si.pop();
31                 
32                 int s = h * (si.empty() ? i : (i - si.top() - 1));
33                 res = max(res, s);
34             }
35             si.push(i);
36         }
37         return res;
38     }
39 };

2. 动态规划

思路同样是从第一行开始一行一行地处理,使[i, j]处最大子矩阵的面积是(right(i, j)-left(i, j))*height(i, j)。其中height统计当前位置及往上'1'的数量;left和right是高度是当前点的height值得左右边界,即是以当前点为中心,以height为高度向两边扩散的左右边界。

递推公式如下:

left(i, j) = max(left(i-1, j), cur_left);
right(i, j) = min(right(i-1, j), cur_right);
height(i, j) = height(i-1, j) + 1, if matrix[i][j]=='1';
height(i, j) = 0, if matrix[i][j]=='0'.

其中cur_left和cur_right的值由当前行决定,如果当前位置是'1',则cur_left和cur_right都不变;如果当前位置是'0',则cur_left就为当前位置的右侧,cur_right就为当前位置(因为左闭右开)。

详见这里

时间复杂度:O(mn)

空间复杂度:O(n)

 1 class Solution
 2 {
 3 public:
 4     int maximalRectangle(vector<vector<char> > &matrix)
 5     {
 6         if(matrix.size() == 0 || matrix[0].size() == 0)
 7             return 0;
 8         
 9         int m = matrix.size(), n = matrix[0].size(), res = 0;
10         vector<int> left(n, 0), right(n, n), height(n, 0);
11         
12         for(int i = 0; i < m; ++ i)
13         {
14             int cur_left = 0, cur_right = n;
15             
16             for(int j = 0; j < n; ++ j)
17                 height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0;
18             
19             for(int j = 0; j < n; ++ j)
20             {
21                 left[j] = matrix[i][j] == '1' ? max(left[j], cur_left) : 0;
22                 cur_left = matrix[i][j] == '1' ? cur_left : j + 1;
23             }
24             
25             for(int j = n - 1; j >= 0; -- j)
26             {
27                 right[j] = matrix[i][j] == '1' ? min(right[j], cur_right) : n;
28                 cur_right = matrix[i][j] == '1' ? cur_right : j;
29             }
30             
31             for(int j = 0; j < n; ++ j)
32                 res = max(res, (right[j] - left[j]) * height[j]);
33         }
34         
35         return res;
36     }
37 };