(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 }
(待续)