본문 바로가기
leetcode

leetcode 85 - Maximal Rectangle

by 초특급하품 2020. 10. 13.

https://leetcode.com/problems/maximal-rectangle/

풀이

모든 시작, 끝에 대해 사각형을 비교해보면 $O(n^4)$만에 찾을 수 있다. 여기서 조금씩 개선해보자.

matrix[i][j]가 있을 때 모든 i, j 쌍에 대해서 비교하지 말고, 아래와 같이 높이 배열을 사용하면 개선할 수 있다.

height[i][j] = matrix[i][j]부터 위에(같은 열) 존재하는 연속된 1의 개수

이렇게 height[i][j]를 정의하면 모든 $n^2$개의 행의 쌍이 아닌 고정된 행에서만 비교하면 되기 때문에 $O(n^3)$만에 구할 수 있다.

 

i는 $O(n)$으로 줄였지만 여전히 height 하나의 행에서 최대 직사각형을 찾기 위해 $O(n^2)$이 필요하다. 모든 인덱스에 대해서 자기가 최대 높이일 때 가능한 최대 너비를 구해서 넓이를 비교하는 시간이다. 여기서는 최대 직사각형의 후보가 될 수 없는 불필요한 연산을 제거하면서 시간을 줄일 수 있다.

임의의 인덱스 i를 기준으로 인덱스는 작은데 높이는 더 큰 인덱스 j가 있을 때, j는 i보다 큰 인덱스 k와 합쳐져서 직사각형을 만들 수 없다. j와 k 사이에 j보다 높이가 작은 i가 있기 때문이다. 반대로 j의 높이가 i보다 작은 경우라면 k와 합쳐져서 직사각형을 만들 수 있다. 

 

이런 인사이트를 가지고 구현을 할 수 있다.

이전 인덱스들의 높이를 오름차순으로 유지하면서 현재 인덱스의 높이를 가장 큰 값으로 갱신할 수 있도록 인덱스를 담는 스택 자료구조를 사용한다. 현재 인덱스의 높이보다 큰 값을 가지는 이전 인덱스들은 스택에서 제거되는데 이때 이 높이로 가질 수 있는 최대 넓이 직사각형을 구해야 한다. 그러려면 너비를 구해야 한다. 가능한 최대 너비의 오른쪽 끝 부분은 현재 인덱스이다. 해당 인덱스를 기준으로 오른쪽 방향으로 가장 먼저 등장하는 해당 높이보다 작은 인덱스이기 때문이다. 최대 너비의 왼쪽 끝 인덱스는 스택에서 두 번째 위에 있는 값이다. 이 값이 해당 인덱스를 기준으로 왼쪽 방향으로 가장 먼저 등장하는 높이가 작은 인덱스이기 때문이다. 

 

이렇게 하면 모든 인덱스에 대해 스택에 push, pop 명령이 한 번씩 발생하여 $O(n)$만에 구할 수 있다. 이를 height의 모든 i에 대해 구해야 하므로 총 시간 복잡도는 $O(n^2)$가 된다.

 

 

코드

class Solution {
public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        if(matrix.size() == 0 || matrix[0].size() == 0) return 0;
        int n = matrix.size(), m = matrix[0].size();
        vector<int> height(m+1);
        
        int ans = 0;
        for(int i = 0; i < n; i++) {
            stack<int> st;
            for(int j = 0; j <= m; j++) {
                if(j == m || matrix[i][j] == '0') height[j] = 0;
                else height[j]++;

                while (!st.empty() && height[st.top()] >= height[j]) {
                    int _height = height[st.top()];
                    st.pop();
                    int _width = st.empty() ? j : j - st.top() - 1;
                    ans = max(ans, _width * _height);
                }
                st.push(j);
            }
        }
        
        return ans;
    }
};

 

'leetcode' 카테고리의 다른 글

leetcode 1402 - Reducing Dishes  (0) 2020.10.15
leetcode 827 - Making A Large Island  (0) 2020.10.13
leetcode 1478 - Allocate Mailboxes  (0) 2020.10.08
leetcode 778 - Swim in Rising Water  (0) 2020.10.07
leetcode 410 - Split Array Largest Sum  (0) 2020.10.06

댓글