Facebook Pixel

2906. Construct Product Matrix

Problem Description

You are given a 2D integer matrix grid of size n × m (0-indexed). Your task is to create a new matrix p of the same size where each element p[i][j] equals the product of all elements in grid except grid[i][j] itself, with the result taken modulo 12345.

In other words, for each position (i, j) in the result matrix:

  • Calculate the product of all elements in the entire grid matrix
  • Exclude only the element at position grid[i][j] from this product
  • Take the result modulo 12345
  • Store this value at p[i][j]

For example, if you have a 2×2 grid:

grid = [[1, 2],
        [3, 4]]

Then p[0][0] would be (2 × 3 × 4) % 12345 = 24, because we multiply all elements except grid[0][0] which is 1.

The challenge is to efficiently compute this for every position in the matrix and return the resulting product matrix p.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The naive approach would be to calculate the product of all elements except grid[i][j] for each position, but this would involve repeated multiplication of the same elements, leading to inefficient O(n²m²) time complexity.

The key insight is that for any position (i, j), the product of all other elements can be broken down into two parts:

  1. The product of all elements that come "before" position (i, j)
  2. The product of all elements that come "after" position (i, j)

This is similar to the classic "Product of Array Except Self" problem, but extended to 2D. We can think of the 2D matrix as a flattened 1D array where we traverse row by row.

For each element, if we know:

  • The product of all elements before it (prefix product)
  • The product of all elements after it (suffix product)

Then the answer for that position is simply prefix × suffix.

To implement this efficiently, we can make two passes through the matrix:

First Pass (Backward): Start from the bottom-right corner and move towards the top-left. For each position, we store the product of all elements that come after it in the traversal order. This gives us the suffix product for each position.

Second Pass (Forward): Start from the top-left corner and move towards the bottom-right. For each position, we multiply the previously calculated suffix product by the running prefix product (product of all elements before the current position).

This way, each element is visited exactly twice, giving us O(nm) time complexity instead of O(n²m²). The modulo operation is applied at each step to prevent overflow and meet the problem requirements.

Learn more about Prefix Sum patterns.

Solution Approach

The solution uses prefix and suffix decomposition to efficiently calculate the product matrix. Here's how the implementation works:

Step 1: Initialize the Result Matrix

n, m = len(grid), len(grid[0])
p = [[0] * m for _ in range(n)]
mod = 12345

We create a result matrix p with the same dimensions as grid and define the modulo value.

Step 2: Calculate Suffix Products (Backward Pass)

suf = 1
for i in range(n - 1, -1, -1):
    for j in range(m - 1, -1, -1):
        p[i][j] = suf
        suf = suf * grid[i][j] % mod
  • Start from the bottom-right corner (n-1, m-1) and traverse backwards
  • For each position (i, j), store the current suf value in p[i][j]
  • Update suf by multiplying it with grid[i][j] and taking modulo
  • At each position, p[i][j] now contains the product of all elements that come after it in row-major order

Step 3: Calculate Prefix Products and Combine (Forward Pass)

pre = 1
for i in range(n):
    for j in range(m):
        p[i][j] = p[i][j] * pre % mod
        pre = pre * grid[i][j] % mod
  • Start from the top-left corner (0, 0) and traverse forward
  • For each position (i, j), multiply the stored suffix product p[i][j] with the current prefix product pre
  • Update pre by multiplying it with grid[i][j] and taking modulo
  • After this step, p[i][j] contains (prefix product) × (suffix product), which equals the product of all elements except grid[i][j]

Why This Works:

  • After the backward pass, p[i][j] contains the product of all elements that come after position (i, j)
  • During the forward pass, pre contains the product of all elements that come before position (i, j)
  • Multiplying these two values gives us the product of all elements except grid[i][j]
  • The modulo operation at each step ensures the values stay within bounds

Time Complexity: O(n × m) - we traverse the matrix twice Space Complexity: O(n × m) - for storing the result matrix

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach.

Consider a 2×3 grid:

grid = [[2, 3, 4],
        [5, 6, 7]]

We want to create matrix p where each p[i][j] is the product of all elements except grid[i][j], modulo 12345.

Step 1: Initialize

  • Create result matrix p with same dimensions (2×3)
  • Set mod = 12345

Step 2: Backward Pass (Calculate Suffix Products)

We traverse from bottom-right to top-left, maintaining suf (suffix product):

Starting with suf = 1:

  • Position (1,2): p[1][2] = 1, then suf = 1 × 7 = 7
  • Position (1,1): p[1][1] = 7, then suf = 7 × 6 = 42
  • Position (1,0): p[1][0] = 42, then suf = 42 × 5 = 210
  • Position (0,2): p[0][2] = 210, then suf = 210 × 4 = 840
  • Position (0,1): p[0][1] = 840, then suf = 840 × 3 = 2520
  • Position (0,0): p[0][0] = 2520, then suf = 2520 × 2 = 5040

After backward pass:

p = [[2520, 840, 210],
     [42,   7,   1]]

Each cell contains the product of all elements that come after it in row-major order.

Step 3: Forward Pass (Calculate Prefix Products and Combine)

We traverse from top-left to bottom-right, maintaining pre (prefix product):

Starting with pre = 1:

  • Position (0,0): p[0][0] = 2520 × 1 = 2520, then pre = 1 × 2 = 2
  • Position (0,1): p[0][1] = 840 × 2 = 1680, then pre = 2 × 3 = 6
  • Position (0,2): p[0][2] = 210 × 6 = 1260, then pre = 6 × 4 = 24
  • Position (1,0): p[1][0] = 42 × 24 = 1008, then pre = 24 × 5 = 120
  • Position (1,1): p[1][1] = 7 × 120 = 840, then pre = 120 × 6 = 720
  • Position (1,2): p[1][2] = 1 × 720 = 720, then pre = 720 × 7 = 5040

Final result:

p = [[2520, 1680, 1260],
     [1008, 840,  720]]

Verification: Let's verify p[0][1] = 1680:

  • Product of all elements except grid[0][1] (which is 3)
  • = 2 × 4 × 5 × 6 × 7 = 1680 ✓

And p[1][1] = 840:

  • Product of all elements except grid[1][1] (which is 6)
  • = 2 × 3 × 4 × 5 × 7 = 840 ✓

The algorithm correctly computes the product of all elements except the current one for each position, using only two passes through the matrix.

Solution Implementation

1class Solution:
2    def constructProductMatrix(self, grid: List[List[int]]) -> List[List[int]]:
3        # Get dimensions of the input grid
4        rows, cols = len(grid), len(grid[0])
5      
6        # Initialize result matrix with zeros
7        result = [[0] * cols for _ in range(rows)]
8      
9        # Modulo value for all calculations
10        MOD = 12345
11      
12        # First pass: Calculate suffix products (from bottom-right to top-left)
13        # For each cell, store the product of all cells that come after it
14        suffix_product = 1
15        for row in range(rows - 1, -1, -1):
16            for col in range(cols - 1, -1, -1):
17                # Store current suffix product in result matrix
18                result[row][col] = suffix_product
19                # Update suffix product for next iteration
20                suffix_product = (suffix_product * grid[row][col]) % MOD
21      
22        # Second pass: Multiply by prefix products (from top-left to bottom-right)
23        # For each cell, multiply its suffix product by the prefix product
24        prefix_product = 1
25        for row in range(rows):
26            for col in range(cols):
27                # Multiply suffix product by prefix product to get final result
28                result[row][col] = (result[row][col] * prefix_product) % MOD
29                # Update prefix product for next iteration
30                prefix_product = (prefix_product * grid[row][col]) % MOD
31      
32        return result
33
1class Solution {
2    public int[][] constructProductMatrix(int[][] grid) {
3        // Define modulo constant for preventing overflow
4        final int MOD = 12345;
5      
6        // Get dimensions of the input grid
7        int rows = grid.length;
8        int cols = grid[0].length;
9      
10        // Initialize result matrix to store products
11        int[][] productMatrix = new int[rows][cols];
12      
13        // First pass: Calculate suffix products (from bottom-right to top-left)
14        // Each cell will initially store the product of all elements after it
15        long suffixProduct = 1;
16        for (int i = rows - 1; i >= 0; i--) {
17            for (int j = cols - 1; j >= 0; j--) {
18                // Store current suffix product at this position
19                productMatrix[i][j] = (int) suffixProduct;
20                // Update suffix product by multiplying with current grid element
21                suffixProduct = (suffixProduct * grid[i][j]) % MOD;
22            }
23        }
24      
25        // Second pass: Multiply by prefix products (from top-left to bottom-right)
26        // This combines prefix and suffix products to get the final result
27        long prefixProduct = 1;
28        for (int i = 0; i < rows; i++) {
29            for (int j = 0; j < cols; j++) {
30                // Multiply stored suffix product by current prefix product
31                productMatrix[i][j] = (int) ((productMatrix[i][j] * prefixProduct) % MOD);
32                // Update prefix product by multiplying with current grid element
33                prefixProduct = (prefixProduct * grid[i][j]) % MOD;
34            }
35        }
36      
37        return productMatrix;
38    }
39}
40
1class Solution {
2public:
3    vector<vector<int>> constructProductMatrix(vector<vector<int>>& grid) {
4        const int MOD = 12345;
5        int rows = grid.size();
6        int cols = grid[0].size();
7      
8        // Initialize result matrix
9        vector<vector<int>> result(rows, vector<int>(cols));
10      
11        // First pass: Calculate suffix products
12        // Traverse from bottom-right to top-left
13        long long suffixProduct = 1;
14        for (int row = rows - 1; row >= 0; --row) {
15            for (int col = cols - 1; col >= 0; --col) {
16                // Store the product of all elements after current position
17                result[row][col] = suffixProduct;
18                // Update suffix product for next iteration
19                suffixProduct = (suffixProduct * grid[row][col]) % MOD;
20            }
21        }
22      
23        // Second pass: Multiply with prefix products
24        // Traverse from top-left to bottom-right
25        long long prefixProduct = 1;
26        for (int row = 0; row < rows; ++row) {
27            for (int col = 0; col < cols; ++col) {
28                // Multiply suffix product with prefix product
29                // This gives product of all elements except current one
30                result[row][col] = (result[row][col] * prefixProduct) % MOD;
31                // Update prefix product for next iteration
32                prefixProduct = (prefixProduct * grid[row][col]) % MOD;
33            }
34        }
35      
36        return result;
37    }
38};
39
1/**
2 * Constructs a product matrix where each element is the product of all elements
3 * in the original grid except the element at the same position, modulo 12345
4 * @param grid - The input 2D number array
5 * @returns A 2D array where each element is the product of all other elements modulo 12345
6 */
7function constructProductMatrix(grid: number[][]): number[][] {
8    const MODULO: number = 12345;
9    const rowCount: number = grid.length;
10    const columnCount: number = grid[0].length;
11  
12    // Initialize the result matrix with zeros
13    const productMatrix: number[][] = Array.from(
14        { length: rowCount }, 
15        () => Array.from({ length: columnCount }, () => 0)
16    );
17  
18    // Calculate suffix products: product of all elements after current position
19    let suffixProduct: number = 1;
20    for (let row: number = rowCount - 1; row >= 0; row--) {
21        for (let col: number = columnCount - 1; col >= 0; col--) {
22            // Store the suffix product for current position
23            productMatrix[row][col] = suffixProduct;
24            // Update suffix product by multiplying with current element
25            suffixProduct = (suffixProduct * grid[row][col]) % MODULO;
26        }
27    }
28  
29    // Calculate prefix products and combine with suffix products
30    let prefixProduct: number = 1;
31    for (let row: number = 0; row < rowCount; row++) {
32        for (let col: number = 0; col < columnCount; col++) {
33            // Multiply suffix product by prefix product to get final result
34            productMatrix[row][col] = (productMatrix[row][col] * prefixProduct) % MODULO;
35            // Update prefix product by multiplying with current element
36            prefixProduct = (prefixProduct * grid[row][col]) % MODULO;
37        }
38    }
39  
40    return productMatrix;
41}
42

Time and Space Complexity

Time Complexity: O(n × m)

The algorithm traverses the entire grid twice:

  • First traversal: iterates through all elements from bottom-right to top-left to calculate suffix products, which takes O(n × m) time
  • Second traversal: iterates through all elements from top-left to bottom-right to combine prefix products with the previously calculated suffix products, which takes O(n × m) time

Total time complexity: O(n × m) + O(n × m) = O(n × m), where n is the number of rows and m is the number of columns in the grid.

Space Complexity: O(1) (excluding the output matrix)

The algorithm uses:

  • The output matrix p of size n × m, which is required for the result and typically not counted in space complexity analysis
  • Three variables: suf, pre, and mod, which use constant space O(1)
  • Variables i, j, n, and m for loop control and dimensions, which use constant space O(1)

If we include the output matrix in the space complexity analysis, it would be O(n × m). However, following the convention stated in the reference answer where the space occupied by the result matrix is ignored, the space complexity is O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow During Multiplication

One of the most common pitfalls in this problem is not handling the modulo operation correctly, which can lead to integer overflow even before applying the modulo.

Incorrect approach:

# Wrong - can cause overflow
suffix_product = suffix_product * grid[row][col]
suffix_product = suffix_product % MOD

Correct approach:

# Right - apply modulo immediately
suffix_product = (suffix_product * grid[row][col]) % MOD

2. Handling Zero Elements

When the grid contains zeros, the product calculation needs special attention. If there's exactly one zero, only that position should have a non-zero value in the result. If there are multiple zeros, all positions should be zero.

Solution:

def constructProductMatrix(self, grid: List[List[int]]) -> List[List[int]]:
    rows, cols = len(grid), len(grid[0])
    result = [[0] * cols for _ in range(rows)]
    MOD = 12345
  
    # Count zeros and track their position
    zero_count = 0
    zero_pos = None
    total_product = 1
  
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == 0:
                zero_count += 1
                zero_pos = (i, j)
            else:
                total_product = (total_product * grid[i][j]) % MOD
  
    # If more than one zero, all results are zero
    if zero_count > 1:
        return result
  
    # If exactly one zero, only that position has non-zero value
    if zero_count == 1:
        result[zero_pos[0]][zero_pos[1]] = total_product
        return result
  
    # Original algorithm for no zeros
    # ... (continue with prefix-suffix approach)

3. Row-Major Order Traversal Confusion

The algorithm assumes row-major order traversal (left-to-right, top-to-bottom). Mixing up the traversal order or indices can lead to incorrect results.

Common mistake:

# Wrong - column-major traversal in suffix pass
for j in range(cols - 1, -1, -1):
    for i in range(rows - 1, -1, -1):
        result[i][j] = suffix_product
        suffix_product = (suffix_product * grid[i][j]) % MOD

Correct approach:

# Right - row-major traversal
for i in range(rows - 1, -1, -1):
    for j in range(cols - 1, -1, -1):
        result[i][j] = suffix_product
        suffix_product = (suffix_product * grid[i][j]) % MOD

4. Modulo Properties with Division

If attempting an alternative approach using total product divided by current element, remember that modular division requires the modular multiplicative inverse, which doesn't always exist.

Problematic approach:

# Wrong - division doesn't work directly with modulo
total_product = calculate_total_product(grid) % MOD
for i in range(rows):
    for j in range(cols):
        result[i][j] = (total_product // grid[i][j]) % MOD  # Incorrect!

The prefix-suffix approach avoids this issue entirely by never requiring division.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement priority queue?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More