[LeetCode] Maximal Rectangle

(Version 1.0)

在做这道题之前,我刚刚做了Scramble String(http://www.cnblogs.com/icecreamdeqinw/p/4338731.html),所以Maximal Rectangle这道题拿来之后我的想法自然跟着惯性觉得肯定是可以用一个三维DP的解法做的,至于时间和空间复杂度能不能通过LeetCode OJ就不好说了。

(一)无脑的三维DP

为了熟悉一下刚刚习得的三维DP的思路样板,先写了一个利用一个三维数组isLine[i][j][k]来保存第i行开头为第j个结尾为第k个元素是否能构成一个全由'1'组成的一条水平线,外层按照线的长度由短到长循环,中间循环这条线的起始位置,然后最内层按照由上至下的顺序移动这条线,这个代码的时间和空间复杂度很高,但是没想到通过了LeetCode OJ,代码如下:

 1 public class Solution {
 2     public int maximalRectangle(char[][] matrix) {
 3         if (matrix.length == 0) {
 4             return 0;
 5         }
 6         boolean[][][] isLine = new boolean[matrix.length][matrix[0].length][matrix[0].length];
 7         for (int i = 0; i < matrix.length; i++) {
 8             for (int j = 0; j < matrix[0].length; j++) {
 9                 if (matrix[i][j] == '1') {
10                     isLine[i][j][j] = true;
11                 }
12             }
13         }
14         int result = 0;
15         for (int i = 0; i < matrix.length; i++) {
16             for (int l = 2; l <= matrix[0].length; l++) {
17                 for (int j = 0; j <= matrix[0].length - l; j++) {
18                     int end = j + l - 1;
19                     if (matrix[i][end] == '1' && isLine[i][j][end - 1]) {
20                         isLine[i][j][end] = true;
21                     }
22                 }
23             }
24         }
25         for (int l = 1; l <= matrix[0].length; l++) { // length of the line
26             for (int i = 0; i <= matrix[0].length - l; i++) { // start point of the line
27                 int bound = i + l - 1;
28                 int area = 0;
29                 for (int j = 0; j < matrix.length; j++) { // row #
30                     if (isLine[j][i][bound]) {
31                         area += l;
32                         result = Math.max(result, area);
33                     } else {
34                         area = 0;
35                     }
36                 }
37             }
38         }
39         return result;
40     }
41 }

这个代码虽然通过了LeetCode OJ,可是实际上是因为这一题给的时间限制非常宽松,大多数通过的Java代码要比这个代码耗时少一半以上,说明这个解法一定不是一个好的解法,需要从不同的思路去优化之。

(二)优化

在用上文提到的无脑三维DP通过OJ之后,看到了LeetCode对于这道题的分类标签中有Stack,想到了也许可以用类似Largest Rectangle in Histogram中使用Stack存储边界的方法,在用不同长度的边从上向下扫的时候,可以不死板地按照长度由小到大进行循环。另外考察上文中代码的循环顺序会发现这其实是一个不cache friendly的代码,因为最内层每次循环时变化的都是第一个下标,所以如果我们把扫的方向从垂直改成水平,并且把三维DP数组改成存垂直线,也许会有性能上的提升。

在看过别人的答案之后,发现原来这道题是Largest Rectangle in Histogram的扩展题,如果我们维护一个二维数组height[][],height[i][j]表示当前矩形中从上边到(i, j)点的距离的话,实际上就是在做matrix.length次Largest Rectangle in Histogram。这里先贴上自己写的代码,随后细说:

 1 public class Solution {
 2     public int maximalRectangle(char[][] matrix) {
 3         if (matrix.length == 0) {
 4             return 0;
 5         }
 6         int[][] height = new int[matrix.length][matrix[0].length];
 7         for (int i = 0; i < matrix[0].length; i++) {
 8             height[0][i] = matrix[0][i] == '1' ? 1 : 0; 
 9         }
10         for (int i = 1; i < matrix.length; i++) {
11             for (int j = 0; j < matrix[0].length; j++) {
12                 height[i][j] = matrix[i][j] == '1' ? height[i - 1][j] + 1 : 0;
13             }
14         }
15         int result = 0;
16         for (int i = 0; i < matrix.length; i++) {
17             Stack<Integer> stack = new Stack<>();
18             for (int j = 0; j < matrix[0].length; j++) {
19                 while (!stack.empty() && height[i][j] < height[i][stack.peek()]) {
20                     result = Math.max(result, height[i][stack.pop()] * (stack.empty() ? j : j - stack.peek() - 1));
21                 }
22                 stack.push(j);
23             }
24             while (!stack.empty()) {
27                 result = Math.max(result, height[i][stack.pop()] * (stack.empty() ? matrix[0].length : matrix[0].length - stack.peek() - 1));
28             }
29         }
30         return result;
31     }
32 }

(待续)

原文地址:https://www.cnblogs.com/icecreamdeqinw/p/4338732.html