Open In App

Largest rectangular sub-matrix whose sum is 0

Improve
Improve
Like Article
Like
Save
Share
Report

Given a 2D matrix, find the largest rectangular sub-matrix whose sum is 0. for example consider the following N x M input matrix 
 

Examples: 

Input :  1,  2,  3
-3, -2, -1
1, 7, 5
Output : 1, 2, 3
-3, -2, -1
Input : 9, 7, 16, 5
1, -6, -7, 3
1, 8, 7, 9
7, -2, 0, 10
Output :-6, -7
8, 7
-2, 0

The naive solution for this problem is to check every possible rectangle in given 2D array. This solution requires 4 nested loops and time complexity of this solution would be O(n^4).

The solution is based on Maximum sum rectangle in a 2D matrix. The idea is to reduce the problem to 1 D array. We can use Hashing to find maximum length of sub-array in 1-D array in O(n) time. We fix the left and right columns one by one and find the largest sub-array with 0 sum contiguous rows for every left and right column pair. We basically find top and bottom row numbers (which have sum is zero) for every fixed left and right column pair. 

To find the top and bottom row numbers, calculate sum of elements in every row from left to right and store these sums in an array say temp[]. So temp[i] indicates sum of elements from left to right in row i. If we find largest subarray with 0 sum on temp, and no. of elements is greater than previous no. of elements then update the values of final row_up, final row_down, final col_left, final col_right.

Implementation:

CPP




// A C++ program to find Largest rectangular
// sub-matrix whose sum is 0
#include <bits/stdc++.h>
using namespace std;
 
const int MAX = 100;
 
// This function basically finds largest 0
// sum subarray in temp[0..n-1]. If 0 sum
// does't exist, then it returns false. Else
// it returns true and sets starting and
// ending indexes as starti and endj.
bool sumZero(int temp[], int* starti, int* endj, int n)
{
    // Map to store the previous sums
    map<int, int> presum;
    int sum = 0; // Initialize sum of elements
 
    // Initialize length of sub-array with sum 0
    int max_length = 0;
 
    // Traverse through the given array
    for (int i = 0; i < n; i++) {
        // Add current element to sum
        sum += temp[i];
 
        if (temp[i] == 0 && max_length == 0) {
            *starti = i;
            *endj = i;
            max_length = 1;
        }
        if (sum == 0) {
            if (max_length < i + 1) {
                *starti = 0;
                *endj = i;
            }
            max_length = i + 1;
        }
 
        // Look for this sum in Hash table
        if (presum.find(sum) != presum.end()) {
            // store previous max_length so
            // that we can check max_length
            // is updated or not
            int old = max_length;
 
            // If this sum is seen before,
            // then update max_len
            max_length = max(max_length, i - presum[sum]);
 
            if (old < max_length) {
                // If max_length is updated then
                // enter and update start and end
                // point of array
                *endj = i;
                *starti = presum[sum] + 1;
            }
        }
        else
 
            // Else insert this sum with
            // index in hash table
            presum[sum] = i;
    }
 
    // Return true if max_length is non-zero
    return (max_length != 0);
}
 
// The main function that finds Largest rectangle
// sub-matrix in a[][] whose sum is 0.
void sumZeroMatrix(int a[][MAX], int row, int col)
{
    int temp[row];
 
    // Variables to store the final output
    int fup = 0, fdown = 0, fleft = 0, fright = 0;
    int sum;
    int up, down;
    int maxl = INT_MIN;
 
    // Set the left column
    for (int left = 0; left < col; left++) {
        // Initialize all elements of temp as 0
        memset(temp, 0, sizeof(temp));
 
        // Set the right column for the left column
        // set by outer loop
        for (int right = left; right < col; right++) {
            // Calculate sum between current left
            // and right for every row 'i'
            for (int i = 0; i < row; i++)
                temp[i] += a[i][right];
 
            // Find largest subarray with 0 sum in
            // temp[]. The sumZero() function also
            // sets values of start and finish. So
            // 'sum' is sum of rectangle between (start,
            // left) and (finish, right) which is
            // boundary columns strictly as left and right.
            bool sum = sumZero(temp, &up, &down, row);
            int ele = (down - up + 1) * (right - left + 1);
 
            // Compare no. of elements with previous
            // no. of elements in sub-Matrix.
            // If new sub-matrix has more elements
            // then update maxl and final boundaries
            // like fup, fdown, fleft, fright
            if (sum && ele > maxl) {
                fup = up;
                fdown = down;
                fleft = left;
                fright = right;
                maxl = ele;
            }
        }
    }
 
    // If there is no change in boundaries
    // than check if a[0][0] is 0
    // If it not zero then print
    // that no such zero-sum sub-matrix exists
    if (fup == 0 && fdown == 0 && fleft == 0 && fright == 0
        && a[0][0] != 0) {
        cout << "No zero-sum sub-matrix exists";
        return;
    }
 
    // Print final values
    for (int j = fup; j <= fdown; j++) {
        for (int i = fleft; i <= fright; i++)
            cout << a[j][i] << " ";
        cout << endl;
    }
}
 
// Driver program to test above functions
int main()
{
    int a[][MAX] = { { 9, 7, 16, 5 },
                     { 1, -6, -7, 3 },
                     { 1, 8, 7, 9 },
                     { 7, -2, 0, 10 } };
 
    int row = 4, col = 4;
    sumZeroMatrix(a, row, col);
    return 0;
}


Java




// A Java program to find Largest rectangular
// sub-matrix whose sum is 0
import java.util.*;
 
public class Main {
    static final int MAX = 100;
 
    // This function basically finds largest 0
    // sum subarray in temp[0..n-1]. If 0 sum
    // does't exist, then it returns false. Else
    // it returns true and sets starting and
    // ending indexes as starti and endj.
    static boolean sumZero(int[] temp, int[] starti,
                           int[] endj, int n)
    {
 
        // Map to store the previous sums
        Map<Integer, Integer> presum = new HashMap<>();
        int sum = 0;
 
        // Initialize length of sub-array with sum 0
        int max_length = 0;
 
        // Traverse through the given array
        for (int i = 0; i < n; i++) {
            sum += temp[i];
 
            if (temp[i] == 0 && max_length == 0) {
                starti[0] = i;
                endj[0] = i;
                max_length = 1;
            }
 
            if (sum == 0) {
                if (max_length < i + 1) {
                    starti[0] = 0;
                    endj[0] = i;
                }
                max_length = i + 1;
            }
 
            // Look for this sum in Hash table
            if (presum.containsKey(sum)) {
 
                // store previous max_length so
                // that we can` check max_length
                // is updated or not
                int old = max_length;
 
                // If this sum is seen before,
                // then update max_len
                max_length = Math.max(max_length,
                                      i - presum.get(sum));
 
                if (old < max_length) {
 
                    // If max_length is updated then
                    // enter and update start and end
                    // point of array
                    endj[0] = i;
                    starti[0] = presum.get(sum) + 1;
                }
            }
            else {
 
                // Else insert this sum with
                // index in hash table
                presum.put(sum, i);
            }
        }
 
        // Return true if max_length is non-zero
        return (max_length != 0);
    }
 
    // The main function that finds Largest rectangle
    // sub-matrix in a[][] whose sum is 0.
    static void sumZeroMatrix(int[][] a, int row, int col)
    {
        int[] temp = new int[row];
 
        // Variables to store the final output
        int fup = 0, fdown = 0, fleft = 0, fright = 0;
        int maxl = Integer.MIN_VALUE;
 
        // Set the left column
        for (int left = 0; left < col; left++) {
 
            // Initialize all elements of temp as 0
            Arrays.fill(temp, 0);
 
            // Set the right column for the left column
            // set by outer loop
            for (int right = left; right < col; right++) {
 
                // Calculate sum between current left
                // and right for every row 'i'
                for (int i = 0; i < row; i++) {
                    temp[i] += a[i][right];
                }
 
                int[] up = new int[1];
                int[] down = new int[1];
 
                // Find largest subarray with 0 sum in
                // temp[]. The sumZero() function also
                // sets values of start and finish. So
                // 'sum' is sum of rectangle between (start,
                // left) and (finish, right) which is
                // boundary columns strictly as left and
                // right.
                boolean s = sumZero(temp, up, down, row);
 
                int ele = (down[0] - up[0] + 1)
                          * (right - left + 1);
 
                // Compare no. of elements with previous
                // no. of elements in sub-Matrix.
                // If new sub-matrix has more elements
                // then update maxl and final boundaries
                // like fup, fdown, fleft, fright
                if (s && ele > maxl) {
                    fup = up[0];
                    fdown = down[0];
                    fleft = left;
                    fright = right;
                    maxl = ele;
                }
            }
        }
 
        // If there is no change in boundaries
        // than check if a[0][0] is 0
        // If it not zero then print
        // that no such zero-sum sub-matrix exists
        if (fup == 0 && fdown == 0 && fleft == 0
            && fright == 0 && a[0][0] != 0) {
            System.out.println(
                "No zero-sum sub-matrix exists");
            return;
        }
 
        // Print final values
        for (int j = fup; j <= fdown; j++) {
            for (int i = fleft; i <= fright; i++) {
                System.out.print(a[j][i] + " ");
            }
            System.out.println();
        }
    }
 
    // Driver program to test above functions
    public static void main(String[] args)
    {
        int[][] a = { { 9, 7, 16, 5 },
                      { 1, -6, -7, 3 },
                      { 1, 8, 7, 9 },
                      { 7, -2, 0, 10 } };
        int row = 4, col = 4;
        sumZeroMatrix(a, row, col);
    }
}
 
// This code is contributed by shiv1o43g


Python3




# A Python program to find Largest rectangular
# sub-matrix whose sum is 0
from typing import List, Tuple
from collections import defaultdict
 
# This function basically finds largest 0
# sum subarray in temp[0..n-1]. If 0 sum
# does't exist, then it returns false. Else
# it returns true and sets starting and
# ending indexes as starti and endj.
 
 
def sum_zero(temp: List[int], starti: List[int], endj: List[int], n: int) -> bool:
    presum = defaultdict(int)
    sum_ = 0
    # Initialize length of sub-array with sum 0
    max_length = 0
    for i in range(n):
        sum_ += temp[i]
        if temp[i] == 0 and max_length == 0:
            starti[0] = i
            endj[0] = i
            max_length = 1
        if sum_ == 0:
            if max_length < i + 1:
                starti[0] = 0
                endj[0] = i
            max_length = i + 1
        if sum_ in presum:
            old = max_length
            max_length = max(max_length, i - presum[sum_])
            if old < max_length:
                endj[0] = i
                starti[0] = presum[sum_] + 1
        else:
            presum[sum_] = i
    return max_length != 0
 
# The main function that finds Largest rectangle
# sub-matrix in a[][] whose sum is 0.
 
 
def sum_zero_matrix(a: List[List[int]], row: int, col: int) -> None:
    temp = [0] * row
    # Variables to store the final output
    fup = fdown = fleft = fright = 0
    maxl = float('-inf')
    # Set the left column
    for left in range(col):
        temp = [0] * row
        # Set the right column for the left column
        # set by outer loop
        for right in range(left, col):
 
            # Calculate sum between current left
            # and right for every row 'i'
            for i in range(row):
                temp[i] += a[i][right]
            up, down = [0], [0]
 
            # Find largest subarray with 0 sum in
            # temp[]. The sumZero() function also
            # sets values of start and finish. So
            # 'sum' is sum of rectangle between (start,
            # left) and (finish, right) which is
            # boundary columns strictly as left and right.
            s = sum_zero(temp, up, down, row)
            ele = (down[0] - up[0] + 1) * (right - left + 1)
 
            # Compare no. of elements with previous
            # no. of elements in sub-Matrix.
            # If new sub-matrix has more elements
            # then update maxl and final boundaries
            # like fup, fdown, fleft, fright
            if s and ele > maxl:
                fup = up[0]
                fdown = down[0]
                fleft = left
                fright = right
                maxl = ele
 
    # If there is no change in boundaries
    # than check if a[0][0] is 0
    # If it not zero then print
    # that no such zero-sum sub-matrix exists
    if fup == fdown == fleft == fright == 0 and a[0][0] != 0:
        # Print final values
        print("No zero-sum sub-matrix exists")
        return
    for j in range(fup, fdown+1):
        for i in range(fleft, fright+1):
            print(a[j][i], end=" ")
        print()
 
 
# Driver program to test above functions
if __name__ == '__main__':
    a = [[9, 7, 16, 5],
         [1, -6, -7, 3],
         [1, 8, 7, 9],
         [7, -2, 0, 10]]
    row, col = 4, 4
    sum_zero_matrix(a, row, col)
# This code is contributed by shivhack999


C#




// A C# program to find Largest rectangular
// sub-matrix whose sum is 0
using System;
using System.Collections.Generic;
 
class Program {
    const int MAX = 100;
 
    // This function basically finds largest 0
    // sum subarray in temp[0..n-1]. If 0 sum
    // does't exist, then it returns false. Else
    // it returns true and sets starting and
    // ending indexes as starti and endj.
    static bool SumZero(int[] temp, out int starti,
                        out int endj, int n)
    {
        var presum = new Dictionary<int, int>();
        int sum = 0;
        int max_length = 0;
 
        starti = -1;
        endj = -1;
 
        for (int i = 0; i < n; i++) {
            sum += temp[i];
 
            if (temp[i] == 0 && max_length == 0) {
                starti = i;
                endj = i;
                max_length = 1;
            }
            if (sum == 0) {
                if (max_length < i + 1) {
                    starti = 0;
                    endj = i;
                }
                max_length = i + 1;
            }
 
            if (presum.TryGetValue(sum, out int j)) {
                int old = max_length;
                max_length = Math.Max(max_length, i - j);
 
                if (old < max_length) {
                    endj = i;
                    starti = presum[sum] + 1;
                }
            }
            else {
                presum[sum] = i;
            }
        }
        return (max_length != 0);
    }
 
    // The main function that finds Largest rectangle
    // sub-matrix in a[][] whose sum is 0.
    static void SumZeroMatrix(int[, ] a, int row, int col)
    {
        int[] temp = new int[row];
 
        int fup = 0, fdown = 0, fleft = 0, fright = 0;
        int sum;
        int up, down;
        int maxl = int.MinValue;
 
        for (int left = 0; left < col; left++) {
            Array.Clear(temp, 0, temp.Length);
 
            for (int right = left; right < col; right++) {
                for (int i = 0; i < row; i++) {
                    temp[i] += a[i, right];
                }
 
                bool sumZero
                    = SumZero(temp, out up, out down, row);
                int ele
                    = (down - up + 1) * (right - left + 1);
 
                if (sumZero && ele > maxl) {
                    fup = up;
                    fdown = down;
                    fleft = left;
                    fright = right;
                    maxl = ele;
                }
            }
        }
 
        if (fup == 0 && fdown == 0 && fleft == 0
            && fright == 0 && a[0, 0] != 0) {
            Console.WriteLine(
                "No zero-sum sub-matrix exists");
            return;
        }
 
        for (int j = fup; j <= fdown; j++) {
            for (int i = fleft; i <= fright; i++) {
                Console.Write(a[j, i] + " ");
            }
            Console.WriteLine();
        }
    }
 
    // Driver program to test above functions
    static void Main()
    {
        int[, ] a = { { 9, 7, 16, 5 },
                      { 1, -6, -7, 3 },
                      { 1, 8, 7, 9 },
                      { 7, -2, 0, 10 } };
 
        int row = 4, col = 4;
        SumZeroMatrix(a, row, col);
    }
}
 
// This Code is contributed by Gaurav_Arora


Javascript




function sumZeroMatrix(matrix) {
    const row = matrix.length;
    const col = matrix[0].length;
 
    // Function to find the largest subarray with 0 sum
    function sumZero(arr, startEnd) {
        const presum = new Map();
        let sum = 0;
        let maxLen = 0;
        let startIndex = 0;
 
        for (let i = 0; i < arr.length; i++) {
            sum += arr[i];
 
            if (arr[i] === 0 && maxLen === 0) {
                startEnd[0] = i;
                startEnd[1] = i;
                maxLen = 1;
            }
 
            if (sum === 0) {
                if (maxLen < i + 1) {
                    startEnd[0] = 0;
                    startEnd[1] = i;
                }
                maxLen = i + 1;
            }
 
            if (presum.has(sum)) {
                const old = maxLen;
                maxLen = Math.max(maxLen, i - presum.get(sum));
                if (old < maxLen) {
                    startEnd[0] = presum.get(sum) + 1;
                    startEnd[1] = i;
                }
            } else {
                presum.set(sum, i);
            }
        }
 
        return maxLen !== 0;
    }
 
    // Initialize variables to store the final output
    let fup = 0, fdown = 0, fleft = 0, fright = 0;
    let maxLen = -Infinity;
 
    // Iterate through columns
    for (let left = 0; left < col; left++) {
        const temp = new Array(row).fill(0);
 
        // Iterate through columns to the right
        for (let right = left; right < col; right++) {
            for (let i = 0; i < row; i++) {
                temp[i] += matrix[i][right];
            }
 
            const startEnd = [0, 0];
            const isZeroSum = sumZero(temp, startEnd);
 
            const elements = (startEnd[1] - startEnd[0] + 1) * (right - left + 1);
 
            if (isZeroSum && elements > maxLen) {
                fup = startEnd[0];
                fdown = startEnd[1];
                fleft = left;
                fright = right;
                maxLen = elements;
            }
        }
    }
 
    // If there is no change in boundaries and the top-left element
    // is not 0, no such zero-sum sub-matrix exists
    if (fup === 0 && fdown === 0 && fleft === 0 && fright === 0 && matrix[0][0] !== 0) {
        console.log("No zero-sum sub-matrix exists");
        return;
    }
 
    // Print the final sub-matrix
    for (let j = fup; j <= fdown; j++) {
        let rowStr = "";
        for (let i = fleft; i <= fright; i++) {
            rowStr += matrix[j][i] + " ";
        }
        console.log(rowStr);
    }
}
 
// Driver program to test the function
const matrix = [
    [9, 7, 16, 5],
    [1, -6, -7, 3],
    [1, 8, 7, 9],
    [7, -2, 0, 10]
];
 
sumZeroMatrix(matrix);


Output

-6 -7 
8 7 
-2 0 


Time Complexity: O(n3)

Auxiliary Space: O(1)

This article is contributed by Harshit Agrawal.  



Last Updated : 26 Oct, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads