Facebook Pixel

2245. Maximum Trailing Zeros in a Cornered Path

Problem Description

You are given a 2D integer array grid of size m x n, where each cell contains a positive integer.

A cornered path is a path through the grid that follows these rules:

  • The path consists of adjacent cells
  • The path can have at most one turn (it can also have no turns)
  • Before the turn (if there is one), the path moves exclusively in one direction - either horizontally (left/right) or vertically (up/down)
  • After the turn, the path must move exclusively in the alternate direction - if it was moving horizontally before, it must move vertically after, and vice versa
  • The path cannot revisit any cell

The product of a path is calculated by multiplying all the values in the cells along that path.

The trailing zeros of a number are the consecutive zeros at the end of the number. For example, 1200 has 2 trailing zeros, and 5040 has 1 trailing zero.

Your task is to find the maximum number of trailing zeros that can be obtained from the product of any valid cornered path in the grid.

Key Points:

  • Horizontal movement means moving left or right
  • Vertical movement means moving up or down
  • A cornered path can be a straight line (0 turns) or have exactly 1 turn
  • You want to maximize the trailing zeros in the product, not the product itself

For example, if a path covers cells with values [2, 5, 10], the product would be 2 × 5 × 10 = 100, which has 2 trailing zeros.

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

Intuition

To understand how to maximize trailing zeros, we need to recognize that trailing zeros come from factors of 10, and 10 = 2 × 5. Therefore, the number of trailing zeros in a product equals min(count of 2s, count of 5s) in the prime factorization.

For example, if we have factors containing three 2s and two 5s, we can form two 10s, giving us 2 trailing zeros.

Now, let's think about the cornered path structure. A cornered path has at most one turn, which means it can only have these shapes:

  • Straight line (no turn): horizontal or vertical
  • L-shaped path (one turn): starts horizontally then turns vertically, or starts vertically then turns horizontally

The key insight is that any cornered path that maximizes the product should start from a boundary and end at another boundary. Why? Because we want to include as many cells as possible to accumulate more factors of 2 and 5.

This leads us to realize that for any cell (i, j) that serves as a turning point, there are exactly 4 possible L-shaped paths:

  1. Start from the left boundary, move right to (i, j), then turn up to the top boundary
  2. Start from the left boundary, move right to (i, j), then turn down to the bottom boundary
  3. Start from the right boundary, move left to (i, j), then turn up to the top boundary
  4. Start from the right boundary, move left to (i, j), then turn down to the bottom boundary

To efficiently calculate the factors of 2 and 5 for any path segment, we can use prefix sums. We precompute:

  • Row-wise prefix sums for counts of 2 and 5 (to handle horizontal segments)
  • Column-wise prefix sums for counts of 2 and 5 (to handle vertical segments)

With these prefix sums, for any turning point (i, j), we can quickly calculate the total count of 2s and 5s for each of the 4 possible L-shaped paths, take the minimum of the two counts to get trailing zeros, and track the maximum across all possibilities.

Learn more about Prefix Sum patterns.

Solution Approach

Based on our intuition, let's implement the solution step by step:

Step 1: Initialize Prefix Sum Arrays

We create four 2D arrays with dimensions (m+1) × (n+1) to store prefix sums:

  • r2[i][j]: count of factor 2 from column 1 to column j in row i
  • r5[i][j]: count of factor 5 from column 1 to column j in row i
  • c2[i][j]: count of factor 2 from row 1 to row i in column j
  • c5[i][j]: count of factor 5 from row 1 to row i in column j

We use 1-indexed arrays (with size m+1 and n+1) to simplify calculations and avoid boundary checks.

Step 2: Build Prefix Sums

For each cell grid[i-1][j-1] (using 1-based indexing in our arrays):

  1. Count the factors of 2 and 5 in the current value:

    while x % 2 == 0:
        x //= 2
        s2 += 1
    while x % 5 == 0:
        x //= 5
        s5 += 1
  2. Update the prefix sums:

    • Row prefix: r2[i][j] = r2[i][j-1] + s2 and r5[i][j] = r5[i][j-1] + s5
    • Column prefix: c2[i][j] = c2[i-1][j] + s2 and c5[i][j] = c5[i-1][j] + s5

Step 3: Enumerate Turning Points

For each cell (i, j) as a potential turning point, calculate the trailing zeros for all 4 possible L-shaped paths:

  1. Path a: Left boundary → (i,j) → Top boundary

    • Horizontal segment: r2[i][j] and r5[i][j] (from column 1 to j in row i)
    • Vertical segment: c2[i-1][j] and c5[i-1][j] (from row 1 to i-1 in column j)
    • Total: min(r2[i][j] + c2[i-1][j], r5[i][j] + c5[i-1][j])
  2. Path b: Left boundary → (i,j) → Bottom boundary

    • Horizontal segment: r2[i][j] and r5[i][j]
    • Vertical segment: c2[m][j] - c2[i][j] and c5[m][j] - c5[i][j] (from row i+1 to m)
    • Total: min(r2[i][j] + c2[m][j] - c2[i][j], r5[i][j] + c5[m][j] - c5[i][j])
  3. Path c: Right boundary → (i,j) → Top boundary

    • Horizontal segment: r2[i][n] - r2[i][j] and r5[i][n] - r5[i][j] (from column j+1 to n)
    • Vertical segment: c2[i][j] and c5[i][j] (from row 1 to i)
    • Total: min(r2[i][n] - r2[i][j] + c2[i][j], r5[i][n] - r5[i][j] + c5[i][j])
  4. Path d: Right boundary → (i,j) → Bottom boundary

    • Horizontal segment: r2[i][n] - r2[i][j-1] and r5[i][n] - r5[i][j-1] (from column j to n, inclusive)
    • Vertical segment: c2[m][j] - c2[i][j] and c5[m][j] - c5[i][j]
    • Total: min(r2[i][n] - r2[i][j-1] + c2[m][j] - c2[i][j], r5[i][n] - r5[i][j-1] + c5[m][j] - c5[i][j])

Note that for path d, we use r2[i][j-1] instead of r2[i][j] to include cell (i,j) in the horizontal segment.

Step 4: Track Maximum

For each turning point, we take the maximum of all four path values and update our answer. The final answer is the maximum trailing zeros across all possible cornered paths in the grid.

The time complexity is O(m × n) for building prefix sums and enumerating turning points, with O(m × n) space for the prefix sum arrays.

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 3×3 grid:

grid = [[4,  5,  2],
        [10, 3,  6],
        [2,  25, 8]]

Step 1: Count factors of 2 and 5 for each cell

First, let's break down each cell into its factors of 2 and 5:

  • 4 = 2² → 2 twos, 0 fives
  • 5 = 5¹ → 0 twos, 1 five
  • 2 = 2¹ → 1 two, 0 fives
  • 10 = 2¹×5¹ → 1 two, 1 five
  • 3 = 3¹ → 0 twos, 0 fives
  • 6 = 2¹×3 → 1 two, 0 fives
  • 2 = 2¹ → 1 two, 0 fives
  • 25 = 5² → 0 twos, 2 fives
  • 8 = 2³ → 3 twos, 0 fives

Step 2: Build prefix sum arrays

Row prefix sums for factors of 2 (r2):

r2 = [[0, 0, 0, 0],
      [0, 2, 2, 3],    // row 1: accumulate 2, 0, 1
      [0, 1, 1, 2],    // row 2: accumulate 1, 0, 1
      [0, 1, 1, 4]]    // row 3: accumulate 1, 0, 3

Row prefix sums for factors of 5 (r5):

r5 = [[0, 0, 0, 0],
      [0, 0, 1, 1],    // row 1: accumulate 0, 1, 0
      [0, 1, 1, 1],    // row 2: accumulate 1, 0, 0
      [0, 0, 2, 2]]    // row 3: accumulate 0, 2, 0

Column prefix sums for factors of 2 (c2):

c2 = [[0, 0, 0, 0],
      [0, 2, 0, 1],    // col accumulations
      [0, 3, 0, 2],
      [0, 4, 0, 5]]

Column prefix sums for factors of 5 (c5):

c5 = [[0, 0, 0, 0],
      [0, 0, 1, 0],    // col accumulations
      [0, 1, 1, 0],
      [0, 1, 3, 0]]

Step 3: Calculate L-shaped paths for turning point (2,2)

Let's examine cell (2,2) with value 3 as a turning point. Using 1-based indexing in our prefix arrays, this is position (2,2).

Path a: Left boundary → (2,2) → Top boundary

  • Horizontal: cells [10, 3] give r2[2][2] = 1 and r5[2][2] = 1
  • Vertical: cell [5] gives c2[1][2] = 0 and c5[1][2] = 1
  • Total factors: 1+0 = 1 two, 1+1 = 2 fives
  • Trailing zeros: min(1, 2) = 1

Path b: Left boundary → (2,2) → Bottom boundary

  • Horizontal: cells [10, 3] give r2[2][2] = 1 and r5[2][2] = 1
  • Vertical: cell [25] gives c2[3][2] - c2[2][2] = 0-0 = 0 and c5[3][2] - c5[2][2] = 3-1 = 2
  • Total factors: 1+0 = 1 two, 1+2 = 3 fives
  • Trailing zeros: min(1, 3) = 1

Path c: Right boundary → (2,2) → Top boundary

  • Horizontal: cell [6] gives r2[2][3] - r2[2][2] = 2-1 = 1 and r5[2][3] - r5[2][2] = 1-1 = 0
  • Vertical: cells [5, 3] give c2[2][2] = 0 and c5[2][2] = 1
  • Total factors: 1+0 = 1 two, 0+1 = 1 five
  • Trailing zeros: min(1, 1) = 1

Path d: Right boundary → (2,2) → Bottom boundary

  • Horizontal: cells [3, 6] give r2[2][3] - r2[2][1] = 2-1 = 1 and r5[2][3] - r5[2][1] = 1-1 = 0
  • Vertical: cell [25] gives c2[3][2] - c2[2][2] = 0-0 = 0 and c5[3][2] - c5[2][2] = 3-1 = 2
  • Total factors: 1+0 = 1 two, 0+2 = 2 fives
  • Trailing zeros: min(1, 2) = 1

Step 4: Check other turning points

Let's check one more promising turning point at (1,2) with value 5:

Path b: Left boundary → (1,2) → Bottom boundary

  • Horizontal: cells [4, 5] give r2[1][2] = 2 and r5[1][2] = 1
  • Vertical: cells [3, 25] give c2[3][2] - c2[1][2] = 0-0 = 0 and c5[3][2] - c5[1][2] = 3-1 = 2
  • Total factors: 2+0 = 2 twos, 1+2 = 3 fives
  • Trailing zeros: min(2, 3) = 2

This gives us 2 trailing zeros! After checking all possible turning points and their L-shaped paths, the maximum trailing zeros would be 2.

Solution Implementation

1class Solution:
2    def maxTrailingZeros(self, grid: List[List[int]]) -> int:
3        rows, cols = len(grid), len(grid[0])
4      
5        # Prefix sum arrays for counting factors of 2 and 5
6        # row_twos[i][j] = count of factor 2 in row i from column 0 to j-1
7        # col_twos[i][j] = count of factor 2 in column j from row 0 to i-1
8        row_twos = [[0] * (cols + 1) for _ in range(rows + 1)]
9        col_twos = [[0] * (cols + 1) for _ in range(rows + 1)]
10        row_fives = [[0] * (cols + 1) for _ in range(rows + 1)]
11        col_fives = [[0] * (cols + 1) for _ in range(rows + 1)]
12      
13        # Build prefix sum arrays by counting factors in each cell
14        for i, row in enumerate(grid, 1):
15            for j, value in enumerate(row, 1):
16                # Count factors of 2 in current cell
17                count_twos = 0
18                while value % 2 == 0:
19                    value //= 2
20                    count_twos += 1
21              
22                # Count factors of 5 in current cell
23                count_fives = 0
24                while value % 5 == 0:
25                    value //= 5
26                    count_fives += 1
27              
28                # Update prefix sums for rows and columns
29                row_twos[i][j] = row_twos[i][j - 1] + count_twos
30                col_twos[i][j] = col_twos[i - 1][j] + count_twos
31                row_fives[i][j] = row_fives[i][j - 1] + count_fives
32                col_fives[i][j] = col_fives[i - 1][j] + count_fives
33      
34        max_trailing_zeros = 0
35      
36        # Check all possible L-shaped paths with turning point at (i, j)
37        for i in range(1, rows + 1):
38            for j in range(1, cols + 1):
39                # Path 1: Left to (i,j), then up
40                path1_zeros = min(
41                    row_twos[i][j] + col_twos[i - 1][j],
42                    row_fives[i][j] + col_fives[i - 1][j]
43                )
44              
45                # Path 2: Left to (i,j), then down
46                path2_zeros = min(
47                    row_twos[i][j] + col_twos[rows][j] - col_twos[i][j],
48                    row_fives[i][j] + col_fives[rows][j] - col_fives[i][j]
49                )
50              
51                # Path 3: Right from (i,j), then up including (i,j)
52                path3_zeros = min(
53                    row_twos[i][cols] - row_twos[i][j] + col_twos[i][j],
54                    row_fives[i][cols] - row_fives[i][j] + col_fives[i][j]
55                )
56              
57                # Path 4: Right from (i,j), then down
58                path4_zeros = min(
59                    row_twos[i][cols] - row_twos[i][j - 1] + col_twos[rows][j] - col_twos[i][j],
60                    row_fives[i][cols] - row_fives[i][j - 1] + col_fives[rows][j] - col_fives[i][j]
61                )
62              
63                # Update maximum trailing zeros
64                max_trailing_zeros = max(
65                    max_trailing_zeros, 
66                    path1_zeros, 
67                    path2_zeros, 
68                    path3_zeros, 
69                    path4_zeros
70                )
71      
72        return max_trailing_zeros
73
1class Solution {
2    public int maxTrailingZeros(int[][] grid) {
3        int rows = grid.length;
4        int cols = grid[0].length;
5      
6        // Prefix sum arrays for counting factors of 2 and 5
7        // rowFactorsOf2[i][j] = count of factor 2 from grid[i-1][0] to grid[i-1][j-1] in row i
8        int[][] rowFactorsOf2 = new int[rows + 1][cols + 1];
9        // colFactorsOf2[i][j] = count of factor 2 from grid[0][j-1] to grid[i-1][j-1] in column j
10        int[][] colFactorsOf2 = new int[rows + 1][cols + 1];
11        // Similarly for factor 5
12        int[][] rowFactorsOf5 = new int[rows + 1][cols + 1];
13        int[][] colFactorsOf5 = new int[rows + 1][cols + 1];
14      
15        // Build prefix sum arrays by counting factors of 2 and 5 for each cell
16        for (int i = 1; i <= rows; i++) {
17            for (int j = 1; j <= cols; j++) {
18                int currentValue = grid[i - 1][j - 1];
19              
20                // Count factors of 2 in current cell
21                int factorOf2Count = 0;
22                while (currentValue % 2 == 0) {
23                    factorOf2Count++;
24                    currentValue /= 2;
25                }
26              
27                // Count factors of 5 in current cell (continue with modified value)
28                int factorOf5Count = 0;
29                while (currentValue % 5 == 0) {
30                    factorOf5Count++;
31                    currentValue /= 5;
32                }
33              
34                // Update prefix sums for current row and column
35                rowFactorsOf2[i][j] = rowFactorsOf2[i][j - 1] + factorOf2Count;
36                colFactorsOf2[i][j] = colFactorsOf2[i - 1][j] + factorOf2Count;
37                rowFactorsOf5[i][j] = rowFactorsOf5[i][j - 1] + factorOf5Count;
38                colFactorsOf5[i][j] = colFactorsOf5[i - 1][j] + factorOf5Count;
39            }
40        }
41      
42        int maxTrailingZeros = 0;
43      
44        // Check all possible L-shaped paths with corner at each cell (i, j)
45        for (int i = 1; i <= rows; i++) {
46            for (int j = 1; j <= cols; j++) {
47                // Path 1: Left + Up (from (i,j) go left to column 1, then up to row 1)
48                int leftUp = Math.min(
49                    rowFactorsOf2[i][j] + colFactorsOf2[i - 1][j],
50                    rowFactorsOf5[i][j] + colFactorsOf5[i - 1][j]
51                );
52              
53                // Path 2: Left + Down (from (i,j) go left to column 1, then down to last row)
54                int leftDown = Math.min(
55                    rowFactorsOf2[i][j] + colFactorsOf2[rows][j] - colFactorsOf2[i][j],
56                    rowFactorsOf5[i][j] + colFactorsOf5[rows][j] - colFactorsOf5[i][j]
57                );
58              
59                // Path 3: Right + Up (from (i,j) go right to last column, then up to row 1)
60                int rightUp = Math.min(
61                    rowFactorsOf2[i][cols] - rowFactorsOf2[i][j] + colFactorsOf2[i][j],
62                    rowFactorsOf5[i][cols] - rowFactorsOf5[i][j] + colFactorsOf5[i][j]
63                );
64              
65                // Path 4: Right + Down (from (i,j) go right to last column, then down to last row)
66                int rightDown = Math.min(
67                    rowFactorsOf2[i][cols] - rowFactorsOf2[i][j - 1] + colFactorsOf2[rows][j] - colFactorsOf2[i][j],
68                    rowFactorsOf5[i][cols] - rowFactorsOf5[i][j - 1] + colFactorsOf5[rows][j] - colFactorsOf5[i][j]
69                );
70              
71                // Update maximum trailing zeros found
72                maxTrailingZeros = Math.max(maxTrailingZeros, 
73                    Math.max(leftUp, Math.max(leftDown, Math.max(rightUp, rightDown))));
74            }
75        }
76      
77        return maxTrailingZeros;
78    }
79}
80
1class Solution {
2public:
3    int maxTrailingZeros(vector<vector<int>>& grid) {
4        int rows = grid.size(), cols = grid[0].size();
5      
6        // Prefix sum arrays for counting factors of 2 and 5
7        // row2[i][j] = count of factor 2 in row i from column 1 to j
8        // col2[i][j] = count of factor 2 in column j from row 1 to i
9        // row5[i][j] = count of factor 5 in row i from column 1 to j
10        // col5[i][j] = count of factor 5 in column j from row 1 to i
11        vector<vector<int>> row2(rows + 1, vector<int>(cols + 1));
12        vector<vector<int>> col2(rows + 1, vector<int>(cols + 1));
13        vector<vector<int>> row5(rows + 1, vector<int>(cols + 1));
14        vector<vector<int>> col5(rows + 1, vector<int>(cols + 1));
15      
16        // Build prefix sum arrays
17        for (int i = 1; i <= rows; ++i) {
18            for (int j = 1; j <= cols; ++j) {
19                int value = grid[i - 1][j - 1];
20                int count2 = 0, count5 = 0;
21              
22                // Count factors of 2 in current cell
23                while (value % 2 == 0) {
24                    value /= 2;
25                    ++count2;
26                }
27              
28                // Count factors of 5 in current cell
29                while (value % 5 == 0) {
30                    value /= 5;
31                    ++count5;
32                }
33              
34                // Update prefix sums
35                row2[i][j] = row2[i][j - 1] + count2;  // Accumulate along row
36                col2[i][j] = col2[i - 1][j] + count2;  // Accumulate along column
37                row5[i][j] = row5[i][j - 1] + count5;  // Accumulate along row
38                col5[i][j] = col5[i - 1][j] + count5;  // Accumulate along column
39            }
40        }
41      
42        int maxZeros = 0;
43      
44        // Check all possible L-shaped paths with each cell as the corner
45        for (int i = 1; i <= rows; ++i) {
46            for (int j = 1; j <= cols; ++j) {
47                // Four possible L-shaped paths:
48                // 1. Left + Up: horizontal from (i,1) to (i,j) + vertical from (1,j) to (i-1,j)
49                int leftUp = min(row2[i][j] + col2[i - 1][j], 
50                                row5[i][j] + col5[i - 1][j]);
51              
52                // 2. Left + Down: horizontal from (i,1) to (i,j) + vertical from (i,j) to (rows,j)
53                int leftDown = min(row2[i][j] + col2[rows][j] - col2[i][j], 
54                                  row5[i][j] + col5[rows][j] - col5[i][j]);
55              
56                // 3. Right + Up: horizontal from (i,j) to (i,cols) + vertical from (1,j) to (i,j)
57                int rightUp = min(row2[i][cols] - row2[i][j] + col2[i][j], 
58                                 row5[i][cols] - row5[i][j] + col5[i][j]);
59              
60                // 4. Right + Down: horizontal from (i,j+1) to (i,cols) + vertical from (i,j) to (rows,j)
61                int rightDown = min(row2[i][cols] - row2[i][j - 1] + col2[rows][j] - col2[i][j], 
62                                   row5[i][cols] - row5[i][j - 1] + col5[rows][j] - col5[i][j]);
63              
64                // Update maximum trailing zeros
65                maxZeros = max({maxZeros, leftUp, leftDown, rightUp, rightDown});
66            }
67        }
68      
69        return maxZeros;
70    }
71};
72
1/**
2 * Finds the maximum number of trailing zeros in the product of numbers along any valid path.
3 * A valid path starts from any cell and moves only right or down, forming an L-shape.
4 * Trailing zeros are determined by min(count of factor 2, count of factor 5) in the product.
5 * 
6 * @param grid - 2D array of positive integers
7 * @returns Maximum number of trailing zeros possible
8 */
9function maxTrailingZeros(grid: number[][]): number {
10    const rows = grid.length;
11    const cols = grid[0].length;
12  
13    // Prefix sum arrays for counting factors of 2 and 5
14    // rowPrefix2[i][j] = sum of factor 2 counts from grid[i-1][0] to grid[i-1][j-1]
15    // colPrefix2[i][j] = sum of factor 2 counts from grid[0][j-1] to grid[i-1][j-1]
16    const rowPrefix2 = Array.from({ length: rows + 1 }, () => new Array(cols + 1).fill(0));
17    const colPrefix2 = Array.from({ length: rows + 1 }, () => new Array(cols + 1).fill(0));
18    const rowPrefix5 = Array.from({ length: rows + 1 }, () => new Array(cols + 1).fill(0));
19    const colPrefix5 = Array.from({ length: rows + 1 }, () => new Array(cols + 1).fill(0));
20  
21    // Build prefix sum arrays by counting factors of 2 and 5 for each cell
22    for (let i = 1; i <= rows; ++i) {
23        for (let j = 1; j <= cols; ++j) {
24            let currentValue = grid[i - 1][j - 1];
25            let countOf2 = 0;
26            let countOf5 = 0;
27          
28            // Count factors of 2 in current cell value
29            while (currentValue % 2 === 0) {
30                ++countOf2;
31                currentValue = Math.floor(currentValue / 2);
32            }
33          
34            // Count factors of 5 in current cell value
35            while (currentValue % 5 === 0) {
36                ++countOf5;
37                currentValue = Math.floor(currentValue / 5);
38            }
39          
40            // Update prefix sums for current row and column
41            rowPrefix2[i][j] = rowPrefix2[i][j - 1] + countOf2;
42            colPrefix2[i][j] = colPrefix2[i - 1][j] + countOf2;
43            rowPrefix5[i][j] = rowPrefix5[i][j - 1] + countOf5;
44            colPrefix5[i][j] = colPrefix5[i - 1][j] + countOf5;
45        }
46    }
47  
48    let maxZeros = 0;
49  
50    // Try each cell as the turning point of the L-shaped path
51    for (let i = 1; i <= rows; ++i) {
52        for (let j = 1; j <= cols; ++j) {
53            // Path type 1: Left to current cell, then up to top
54            const leftUp = Math.min(
55                rowPrefix2[i][j] + colPrefix2[i - 1][j],
56                rowPrefix5[i][j] + colPrefix5[i - 1][j]
57            );
58          
59            // Path type 2: Left to current cell, then down to bottom
60            const leftDown = Math.min(
61                rowPrefix2[i][j] + colPrefix2[rows][j] - colPrefix2[i][j],
62                rowPrefix5[i][j] + colPrefix5[rows][j] - colPrefix5[i][j]
63            );
64          
65            // Path type 3: Right from current cell, then up (including current cell)
66            const rightUp = Math.min(
67                rowPrefix2[i][cols] - rowPrefix2[i][j] + colPrefix2[i][j],
68                rowPrefix5[i][cols] - rowPrefix5[i][j] + colPrefix5[i][j]
69            );
70          
71            // Path type 4: Right from current cell, then down (excluding double count)
72            const rightDown = Math.min(
73                rowPrefix2[i][cols] - rowPrefix2[i][j - 1] + colPrefix2[rows][j] - colPrefix2[i][j],
74                rowPrefix5[i][cols] - rowPrefix5[i][j - 1] + colPrefix5[rows][j] - colPrefix5[i][j]
75            );
76          
77            // Update maximum trailing zeros
78            maxZeros = Math.max(maxZeros, leftUp, leftDown, rightUp, rightDown);
79        }
80    }
81  
82    return maxZeros;
83}
84

Time and Space Complexity

Time Complexity: O(m × n)

The algorithm consists of two main phases:

  1. Preprocessing phase: The first nested loop iterates through all m × n cells of the grid. For each cell, it counts the factors of 2 and 5, which takes O(log(max_value)) time per cell. Since the maximum value in the grid is bounded, this is effectively O(1) per cell. The loop also updates the prefix sum arrays r2, c2, r5, and c5 in O(1) time per cell. Therefore, this phase takes O(m × n) time.

  2. Computation phase: The second nested loop again iterates through all m × n cells. For each cell (i, j), it computes four possible L-shaped paths using the precomputed prefix sums. Each computation involves constant-time arithmetic operations using the prefix sum arrays. Therefore, this phase also takes O(m × n) time.

The total time complexity is O(m × n) + O(m × n) = O(m × n).

Space Complexity: O(m × n)

The algorithm uses four 2D arrays (r2, c2, r5, c5), each of size (m + 1) × (n + 1). Each array stores prefix sums for either factor 2 or factor 5, either row-wise or column-wise. The space required is 4 × (m + 1) × (n + 1) = O(m × n).

The additional variables (ans, a, b, c, d, s2, s5) use O(1) space.

Therefore, the total space complexity is O(m × n).

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

Common Pitfalls

1. Incorrect Handling of Path Inclusion at Turning Point

One of the most common mistakes is incorrectly including or excluding the turning point cell when calculating the path segments. This leads to either double-counting the turning point or missing it entirely.

The Problem: When calculating L-shaped paths, the turning point cell (i,j) should be counted exactly once. However, it's easy to make mistakes like:

  • Double-counting: Including it in both horizontal and vertical segments
  • Missing it: Excluding it from both segments
  • Inconsistent handling: Different inclusion logic for different path types

Example of the Bug:

# Incorrect: Path 3 - Right from (i,j) then up
# This excludes the turning point from both segments!
path3_zeros = min(
    row_twos[i][cols] - row_twos[i][j],  # Excludes column j
    col_twos[i-1][j]                      # Excludes row i
)

The Solution: The turning point should be included in exactly one segment. The correct approach:

# Correct: Include turning point in vertical segment
path3_zeros = min(
    row_twos[i][cols] - row_twos[i][j] + col_twos[i][j],
    row_fives[i][cols] - row_fives[i][j] + col_fives[i][j]
)
# Here col_twos[i][j] includes the turning point

2. Confusion with Prefix Sum Boundaries

Another pitfall is misunderstanding what the prefix sum values represent, especially with 1-based indexing.

The Problem: With 1-based indexing:

  • row_twos[i][j] represents the sum from column 1 to column j in row i
  • col_twos[i][j] represents the sum from row 1 to row i in column j

It's easy to forget whether boundaries are inclusive or exclusive.

Example of the Bug:

# Incorrect: Trying to get sum from column j+1 to n
path3_horizontal = row_twos[i][cols] - row_twos[i][j+1]  # Wrong!

The Solution: Remember that to get the sum from column j+1 to n, you subtract the prefix sum up to column j:

# Correct: Sum from column j+1 to n
path3_horizontal = row_twos[i][cols] - row_twos[i][j]

3. Not Considering Straight Line Paths

The problem states paths can have "at most one turn," meaning straight lines (0 turns) are also valid.

The Problem: The code only considers L-shaped paths with exactly one turn, missing potentially better straight-line paths.

The Solution: Add checks for straight horizontal and vertical paths:

# Add straight line paths
for i in range(1, rows + 1):
    # Horizontal line across entire row
    horizontal_zeros = min(row_twos[i][cols], row_fives[i][cols])
    max_trailing_zeros = max(max_trailing_zeros, horizontal_zeros)

for j in range(1, cols + 1):
    # Vertical line down entire column
    vertical_zeros = min(col_twos[rows][j], col_fives[rows][j])
    max_trailing_zeros = max(max_trailing_zeros, vertical_zeros)

4. Integer Overflow in Factor Counting

While less common in Python, continuously dividing by 2 or 5 without bounds checking could theoretically cause issues with very large numbers.

The Solution: Add a reasonable upper bound or use more efficient factor counting:

def count_factor(n, factor):
    count = 0
    while n > 0 and n % factor == 0:
        n //= factor
        count += 1
    return count
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What does the following code do?

1def f(arr1, arr2):
2  i, j = 0, 0
3  new_arr = []
4  while i < len(arr1) and j < len(arr2):
5      if arr1[i] < arr2[j]:
6          new_arr.append(arr1[i])
7          i += 1
8      else:
9          new_arr.append(arr2[j])
10          j += 1
11  new_arr.extend(arr1[i:])
12  new_arr.extend(arr2[j:])
13  return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2  int i = 0, j = 0;
3  List<Integer> newArr = new ArrayList<>();
4
5  while (i < arr1.length && j < arr2.length) {
6      if (arr1[i] < arr2[j]) {
7          newArr.add(arr1[i]);
8          i++;
9      } else {
10          newArr.add(arr2[j]);
11          j++;
12      }
13  }
14
15  while (i < arr1.length) {
16      newArr.add(arr1[i]);
17      i++;
18  }
19
20  while (j < arr2.length) {
21      newArr.add(arr2[j]);
22      j++;
23  }
24
25  return newArr;
26}
27
1function f(arr1, arr2) {
2  let i = 0, j = 0;
3  let newArr = [];
4  
5  while (i < arr1.length && j < arr2.length) {
6      if (arr1[i] < arr2[j]) {
7          newArr.push(arr1[i]);
8          i++;
9      } else {
10          newArr.push(arr2[j]);
11          j++;
12      }
13  }
14  
15  while (i < arr1.length) {
16      newArr.push(arr1[i]);
17      i++;
18  }
19  
20  while (j < arr2.length) {
21      newArr.push(arr2[j]);
22      j++;
23  }
24  
25  return newArr;
26}
27

Recommended Readings

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

Load More