1727. Largest Submatrix With Rearrangements - Explanation

Problem Link

Description

You are given a binary matrix matrix of size m x n, and you are allowed to rearrange the columns of the matrix in any order.

Return the area of the largest submatrix within matrix where every element of the submatrix is 1 after reordering the columns optimally.

Example 1:

Input: matrix = [
    [0,0,1],
    [1,1,1],
    [1,0,1]
]

Output: 3

Explanation: You can rearrange the columns as shown above.
The largest submatrix of 1s, in bold, has an area of 3.

Example 2:

Input: matrix = [
    [1,1,0],
    [1,0,1]
]

Output: 2

Explanation: Notice that you must rearrange entire columns, and there is no way to make a submatrix of 1s larger than an area of 2.

Constraints:

  • 1 <= (matrix.length * matrix[i].length) <= 100,000
  • matrix[i][j] is either 0 or 1.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • 2D Matrix Traversal - Understanding how to iterate through rows and columns of a matrix
  • Greedy Algorithms - Making locally optimal choices to find a global optimum
  • Sorting - Sorting arrays and understanding how sorted order enables efficient computations
  • Histogram/Height Arrays - Building cumulative heights column-wise (similar to "Largest Rectangle in Histogram")

1. Brute Force

Intuition

We can rearrange columns in any order, so the key is to find which columns can form a rectangle of all 1s. For each starting row, we track which columns have continuous 1s from that row downward. As we extend the rectangle row by row, columns with a 0 are eliminated. The area at each step is the number of surviving columns multiplied by the current height.

Algorithm

  1. For each starting row, initialize a set containing all column indices.
  2. Iterate through each subsequent row:
    • Remove columns that have a 0 in the current row.
    • Calculate the area as (remaining columns) * (current height).
    • Update the maximum area found.
  3. Return the maximum area.
class Solution:
    def largestSubmatrix(self, matrix: List[List[int]]) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        res = 0

        for start_row in range(ROWS):
            ones = deque(list(range(COLS)))

            for r in range(start_row, ROWS):
                if not ones:
                    break
                for _ in range(len(ones)):
                    c = ones.popleft()
                    if matrix[r][c] == 1:
                        ones.append(c)

                res = max(res, len(ones) * (r - start_row + 1))

        return res

Time & Space Complexity

  • Time complexity: O(mn2)O(m * n ^ 2)
  • Space complexity: O(n)O(n)

Where mm is the number of rows and nn is the number of columns.


2. Greedy + Sorting

Intuition

Think of each cell as the height of a bar extending upward through consecutive 1s. For each row, compute these heights based on the previous row. Since we can rearrange columns, sort the heights in descending order. Then greedily compute the largest rectangle: the first column can use its full height, the first two columns are limited by the second height, and so on.

Algorithm

  1. Maintain a heights array tracking consecutive 1s above each cell.
  2. For each row:
    • Update heights: if the cell is 1, add the previous height; otherwise reset to 0.
    • Sort heights in descending order.
    • For each position i, compute area as (i + 1) * heights[i] and track the maximum.
  3. Return the maximum area found.
class Solution:
    def largestSubmatrix(self, matrix: List[List[int]]) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        res = 0
        prev_heights = [0] * COLS

        for r in range(ROWS):
            heights = matrix[r][:]
            for c in range(COLS):
                if heights[c] > 0:
                    heights[c] += prev_heights[c]

            sorted_heights = sorted(heights, reverse=True)
            for i in range(COLS):
                res = max(res, (i + 1) * sorted_heights[i])

            prev_heights = heights

        return res

Time & Space Complexity

  • Time complexity: O(mnlogn)O(m * n \log n)
  • Space complexity: O(n)O(n)

Where mm is the number of rows and nn is the number of columns.


3. Greedy + Sorting (Overwriting the Input)

Intuition

This is the same approach as above but optimizes space by modifying the input matrix directly. Each cell stores the cumulative height of consecutive 1s ending at that cell. This eliminates the need for a separate heights array.

Algorithm

  1. For each row starting from the second:
    • If a cell is 1, add the value from the cell directly above it.
  2. For each row:
    • Sort the row in descending order.
    • Compute the maximum area using the greedy formula.
  3. Return the maximum area.
class Solution:
    def largestSubmatrix(self, matrix: List[List[int]]) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        res = 0

        for r in range(1, ROWS):
            for c in range(COLS):
                if matrix[r][c]:
                    matrix[r][c] += matrix[r - 1][c]

        for r in range(ROWS):
            matrix[r].sort(reverse=True)
            for i in range(COLS):
                res = max(res, (i + 1) * matrix[r][i])

        return res

Time & Space Complexity

  • Time complexity: O(mnlogn)O(m * n \log n)
  • Space complexity: O(1)O(1) or O(n)O(n) depending on the sorting algoirhtm.

Where mm is the number of rows and nn is the number of columns.


4. Greedy

Intuition

We can avoid sorting entirely by maintaining a sorted order implicitly. Track the column indices that had continuous 1s from the previous row. For the current row, first process columns from the previous list (they have taller heights), then add new columns that just started with a 1. This naturally keeps columns ordered by height in descending order.

Algorithm

  1. Maintain a list of column indices from the previous row that had continuous 1s.
  2. For each row:
    • Create a new list starting with columns from the previous list that still have a 1 (incrementing their height in the matrix).
    • Append columns where the current cell is 1 but was 0 before (new columns starting at height 1).
    • Compute the maximum area: position i gives area (i + 1) * matrix[r][heights[i]].
  3. Return the maximum area.
class Solution:
    def largestSubmatrix(self, matrix: List[List[int]]) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        res = 0
        prevHeights = []

        for r in range(ROWS):
            heights = []
            for c in prevHeights:
                if matrix[r][c]:
                    matrix[r][c] += matrix[r - 1][c]
                    heights.append(c)

            for c in range(COLS):
                if matrix[r][c] == 1:
                    heights.append(c)

            for i, c in enumerate(heights):
                res = max(res, (i + 1) * matrix[r][c])

            prevHeights = heights

        return res

Time & Space Complexity

  • Time complexity: O(mn)O(m * n)
  • Space complexity: O(n)O(n)

Where mm is the number of rows and nn is the number of columns.


Common Pitfalls

Resetting Heights on Zero Cells

When building the height array row by row, encountering a 0 should reset the height to 0 for that column. A common mistake is to simply add the previous height regardless of the current cell value, which incorrectly extends heights through 0 cells and produces invalid rectangles.

Forgetting Column Rearrangement is Allowed

Unlike the standard largest rectangle in histogram problem, columns can be rearranged here. Attempting to use a monotonic stack approach without sorting the heights misses the key insight. Sorting heights in descending order allows greedy computation of the maximum area at each width.

Incorrect Area Formula After Sorting

After sorting heights in descending order, the area at position i is (i + 1) * heights[i], not i * heights[i]. The width is the number of columns considered so far (1-indexed), so using i instead of i + 1 underestimates the width by one column.