Find largest rectangle containing only zeros in an N×N binary matrix

前端 未结 8 2205
伪装坚强ぢ
伪装坚强ぢ 2020-11-22 06:25

Given an NxN binary matrix (containing only 0\'s or 1\'s), how can we go about finding largest rectangle containing all 0\'s?

Example:

      I
    0          


        
相关标签:
8条回答
  • 2020-11-22 06:55

    To be complete, here's the C# version which outputs the rectangle coordinates. It's based on dmarra's answer but without any other dependencies. There's only the function bool GetPixel(int x, int y), which returns true when a pixel is set at the coordinates x,y.

        public struct INTRECT
        {
            public int Left, Right, Top, Bottom;
    
            public INTRECT(int aLeft, int aTop, int aRight, int aBottom)
            {
                Left = aLeft;
                Top = aTop;
                Right = aRight;
                Bottom = aBottom;
            }
    
            public int Width { get { return (Right - Left + 1); } }
    
            public int Height { get { return (Bottom - Top + 1); } }
    
            public bool IsEmpty { get { return Left == 0 && Right == 0 && Top == 0 && Bottom == 0; } }
    
            public static bool operator ==(INTRECT lhs, INTRECT rhs)
            {
                return lhs.Left == rhs.Left && lhs.Top == rhs.Top && lhs.Right == rhs.Right && lhs.Bottom == rhs.Bottom;
            }
    
            public static bool operator !=(INTRECT lhs, INTRECT rhs)
            {
                return !(lhs == rhs);
            }
    
            public override bool Equals(Object obj)
            {
                return obj is INTRECT && this == (INTRECT)obj;
            }
    
            public bool Equals(INTRECT obj)
            {
                return this == obj;
            }
    
            public override int GetHashCode()
            {
                return Left.GetHashCode() ^ Right.GetHashCode() ^ Top.GetHashCode() ^ Bottom.GetHashCode();
            }
        }
    
        public INTRECT GetMaximumFreeRectangle()
        {
            int XEnd = 0;
            int YStart = 0;
            int MaxRectTop = 0;
            INTRECT MaxRect = new INTRECT();
            // STEP 1:
            // build a seed histogram using the first row of grid points
            // example: [true, true, false, true] = [1,1,0,1]
            int[] hist = new int[Height];
            for (int y = 0; y < Height; y++)
            {
                if (!GetPixel(0, y))
                {
                    hist[y] = 1;
                }
            }
    
            // STEP 2:
            // get a starting max area from the seed histogram we created above.
            // using the example from above, this value would be [1, 1], as the only valid area is a single point.
            // another example for [0,0,0,1,0,0] would be [1, 3], because the largest area of contiguous free space is 3.
            // Note that at this step, the heigh fo the found rectangle will always be 1 because we are operating on
            // a single row of data.
            Tuple<int, int> maxSize = MaxRectSize(hist, out YStart);
            int maxArea = (int)(maxSize.Item1 * maxSize.Item2);
            MaxRectTop = YStart;
            // STEP 3:
            // build histograms for each additional row, re-testing for new possible max rectangluar areas
            for (int x = 1; x < Width; x++)
            {
                // build a new histogram for this row. the values of this row are
                // 0 if the current grid point is occupied; otherwise, it is 1 + the value
                // of the previously found historgram value for the previous position. 
                // What this does is effectly keep track of the height of continous avilable spaces.
                // EXAMPLE:
                //      Given the following grid data (where 1 means occupied, and 0 means free; for clairty):
                //          INPUT:        OUTPUT:
                //      1.) [0,0,1,0]   = [1,1,0,1]
                //      2.) [0,0,1,0]   = [2,2,0,2]
                //      3.) [1,1,0,1]   = [0,0,1,0]
                //
                //  As such, you'll notice position 1,0 (row 1, column 0) is 2, because this is the height of contiguous
                //  free space.
                for (int y = 0; y < Height; y++)
                {
                    if (!GetPixel(x, y))
                    {
                        hist[y]++;
                    }
                    else
                    {
                        hist[y] = 0;
                    }
                }
    
                // find the maximum size of the current histogram. If it happens to be larger
                // that the currently recorded max size, then it is the new max size.
                Tuple<int, int> maxSizeTemp = MaxRectSize(hist, out YStart);
                int tempArea = (int)(maxSizeTemp.Item1 * maxSizeTemp.Item2);
                if (tempArea > maxArea)
                {
                    maxSize = maxSizeTemp;
                    maxArea = tempArea;
                    MaxRectTop = YStart;
                    XEnd = x;
                }
            }
            MaxRect.Left = XEnd - maxSize.Item1 + 1;
            MaxRect.Top = MaxRectTop;
            MaxRect.Right = XEnd;
            MaxRect.Bottom = MaxRectTop + maxSize.Item2 - 1;
    
            // at this point, we know the max size
            return MaxRect;
        }
    
        private Tuple<int, int> MaxRectSize(int[] histogram, out int YStart)
        {
            Tuple<int, int> maxSize = new Tuple<int, int>(0, 0);
            int maxArea = 0;
            Stack<Tuple<int, int>> stack = new Stack<Tuple<int, int>>();
            int x = 0;
            YStart = 0;
            for (x = 0; x < histogram.Length; x++)
            {
                int start = x;
                int height = histogram[x];
                while (true)
                {
                    if (stack.Count == 0 || height > stack.Peek().Item2)
                    {
                        stack.Push(new Tuple<int, int>(start, height));
                    }
                    else if (height < stack.Peek().Item2)
                    {
                        int tempArea = (int)(stack.Peek().Item2 * (x - stack.Peek().Item1));
                        if (tempArea > maxArea)
                        {
                            YStart = stack.Peek().Item1;
                            maxSize = new Tuple<int, int>(stack.Peek().Item2, (x - stack.Peek().Item1));
                            maxArea = tempArea;
                        }
                        Tuple<int, int> popped = stack.Pop();
                        start = (int)popped.Item1;
                        continue;
                    }
                    break;
                }
            }
    
            foreach (Tuple<int, int> data in stack)
            {
                int tempArea = (int)(data.Item2 * (x - data.Item1));
                if (tempArea > maxArea)
                {
                    YStart = data.Item1;
                    maxSize = new Tuple<int, int>(data.Item2, (x - data.Item1));
                    maxArea = tempArea;
                }
            }
    
            return maxSize;
        }
    
    0 讨论(0)
  • 2020-11-22 06:59

    Solution with space complexity O(columns) [Can be modified to O(rows) also] and time complexity O(rows*columns)

    public int maximalRectangle(char[][] matrix) {
        int m = matrix.length;
        if (m == 0)
            return 0;
        int n = matrix[0].length;
        int maxArea = 0;
        int[] aux = new int[n];
        for (int i = 0; i < n; i++) {
            aux[i] = 0;
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                aux[j] = matrix[i][j] - '0' + aux[j];
                maxArea = Math.max(maxArea, maxAreaHist(aux));
            }
        }
        return maxArea;
    }
    
    public int maxAreaHist(int[] heights) {
        int n = heights.length;
        Stack<Integer> stack = new Stack<Integer>();
        stack.push(0);
        int maxRect = heights[0];
        int top = 0;
        int leftSideArea = 0;
        int rightSideArea = heights[0];
        for (int i = 1; i < n; i++) {
            if (stack.isEmpty() || heights[i] >= heights[stack.peek()]) {
                stack.push(i);
            } else {
                while (!stack.isEmpty() && heights[stack.peek()] > heights[i]) {
                    top = stack.pop();
                    rightSideArea = heights[top] * (i - top);
                    leftSideArea = 0;
                    if (!stack.isEmpty()) {
                        leftSideArea = heights[top] * (top - stack.peek() - 1);
                    } else {
                        leftSideArea = heights[top] * top;
                    }
                    maxRect = Math.max(maxRect, leftSideArea + rightSideArea);
                }
                stack.push(i);
            }
        }
        while (!stack.isEmpty()) {
            top = stack.pop();
            rightSideArea = heights[top] * (n - top);
            leftSideArea = 0;
            if (!stack.isEmpty()) {
                leftSideArea = heights[top] * (top - stack.peek() - 1);
            } else {
                leftSideArea = heights[top] * top;
            }
            maxRect = Math.max(maxRect, leftSideArea + rightSideArea);
        }
        return maxRect;
    }
    

    But I get Time Limite exceeded excpetion when I try this on LeetCode. Is there any less complex solution?

    0 讨论(0)
提交回复
热议问题