Facebook Pixel

562. Longest Line of Consecutive One in Matrix 🔒

Problem Description

You are given an m x n binary matrix mat containing only 0s and 1s. Your task is to find the length of the longest line of consecutive 1s in the matrix.

A line of consecutive 1s can be formed in four possible directions:

  • Horizontal: A sequence of 1s in the same row (left to right)
  • Vertical: A sequence of 1s in the same column (top to bottom)
  • Diagonal: A sequence of 1s going from top-left to bottom-right
  • Anti-diagonal: A sequence of 1s going from top-right to bottom-left

The solution uses dynamic programming with four 2D arrays (a, b, c, d) to track the length of consecutive 1s ending at each position in all four directions:

  • a[i][j]: Length of consecutive 1s ending at position (i,j) in the vertical direction
  • b[i][j]: Length of consecutive 1s ending at position (i,j) in the horizontal direction
  • c[i][j]: Length of consecutive 1s ending at position (i,j) in the diagonal direction
  • d[i][j]: Length of consecutive 1s ending at position (i,j) in the anti-diagonal direction

The arrays are padded with extra rows and columns (size (m+2) x (n+2)) to handle boundary cases. For each cell containing a 1 in the original matrix, the algorithm updates the consecutive count by adding 1 to the count from the previous cell in each respective direction:

  • Vertical: a[i][j] = a[i-1][j] + 1
  • Horizontal: b[i][j] = b[i][j-1] + 1
  • Diagonal: c[i][j] = c[i-1][j-1] + 1
  • Anti-diagonal: d[i][j] = d[i-1][j+1] + 1

The maximum value among all these counts across all positions gives the answer.

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

Intuition

The key insight is that finding the longest line of consecutive 1s is essentially a counting problem where we need to track how many 1s we've seen in a row along different directions.

Think about how you would manually count consecutive 1s in a line - you'd start from a position and keep counting as long as you encounter 1s. When you hit a 0, your count resets to zero. This naturally suggests a dynamic programming approach where we build upon previous counts.

For any cell containing a 1, the length of the consecutive 1s ending at that cell depends on the length of consecutive 1s ending at the previous cell in the same direction. This creates a recurrence relation:

  • If the current cell is 1, we extend the line from the previous cell by 1
  • If the current cell is 0, the line breaks and the count becomes 0

Since we need to find the longest line in any of the four directions (horizontal, vertical, diagonal, anti-diagonal), we need to track counts for all four directions simultaneously. Each direction has its own "previous cell":

  • Horizontal line: previous cell is to the left (i, j-1)
  • Vertical line: previous cell is above (i-1, j)
  • Diagonal line: previous cell is top-left (i-1, j-1)
  • Anti-diagonal line: previous cell is top-right (i-1, j+1)

The padding with extra rows and columns is a clever implementation detail to avoid boundary checks. By initializing the padded borders with 0s, we ensure that any line starting at the edge of the actual matrix begins with a count of 0+1=1, which is correct.

As we traverse the matrix once, we continuously update our running counts for all four directions at each cell, keeping track of the maximum seen so far. This gives us an efficient O(m*n) solution with a single pass through the matrix.

Learn more about Dynamic Programming patterns.

Solution Approach

The solution implements dynamic programming using four 2D arrays to track consecutive 1s in each direction. Here's how the implementation works:

Data Structure Setup:

  • Create four (m+2) x (n+2) arrays initialized with zeros: a, b, c, d
  • The extra padding (adding 2 to dimensions) eliminates the need for boundary checking
  • Each array represents one direction:
    • a: vertical lines
    • b: horizontal lines
    • c: diagonal lines (top-left to bottom-right)
    • d: anti-diagonal lines (top-right to bottom-left)

Core Algorithm:

  1. Iterate through the original matrix using indices i from 1 to m and j from 1 to n
  2. For each cell, check if mat[i-1][j-1] equals 1 (adjusting indices due to padding)
  3. If it's a 1, update all four direction arrays using the recurrence relations:
    • a[i][j] = a[i-1][j] + 1 (extend vertical line from above)
    • b[i][j] = b[i][j-1] + 1 (extend horizontal line from left)
    • c[i][j] = c[i-1][j-1] + 1 (extend diagonal line from top-left)
    • d[i][j] = d[i-1][j+1] + 1 (extend anti-diagonal line from top-right)
  4. Update the answer with the maximum of all four values and the current answer

Pattern Used: This follows the classic DP pattern where dp[i][j] depends on previously computed values. The state transition is straightforward:

  • If current cell is 1: dp[current] = dp[previous] + 1
  • If current cell is 0: dp[current] = 0 (implicitly handled by initialization)

Time and Space Complexity:

  • Time: O(m * n) - single pass through the matrix
  • Space: O(m * n) - four auxiliary arrays of size (m+2) x (n+2)

The elegance of this solution lies in processing all four directions simultaneously in a single pass, avoiding the need for multiple traversals or complex boundary conditions.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

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

Consider this 3×3 matrix:

mat = [[0, 1, 1],
       [1, 1, 0],
       [1, 0, 1]]

Step 1: Initialize four padded arrays (5×5 with zeros)

a = b = c = d = 
[[0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0]]

Step 2: Process each cell

Position (0,0) - mat value is 0:

  • All arrays remain 0 at position [1][1]
  • Current max = 0

Position (0,1) - mat value is 1:

  • a[1][2] = a[0][2] + 1 = 0 + 1 = 1 (vertical)
  • b[1][2] = b[1][1] + 1 = 0 + 1 = 1 (horizontal)
  • c[1][2] = c[0][1] + 1 = 0 + 1 = 1 (diagonal)
  • d[1][2] = d[0][3] + 1 = 0 + 1 = 1 (anti-diagonal)
  • Current max = 1

Position (0,2) - mat value is 1:

  • a[1][3] = a[0][3] + 1 = 0 + 1 = 1 (vertical)
  • b[1][3] = b[1][2] + 1 = 1 + 1 = 2 (horizontal - extends from left!)
  • c[1][3] = c[0][2] + 1 = 0 + 1 = 1 (diagonal)
  • d[1][3] = d[0][4] + 1 = 0 + 1 = 1 (anti-diagonal)
  • Current max = 2

Position (1,0) - mat value is 1:

  • a[2][1] = a[1][1] + 1 = 0 + 1 = 1 (vertical)
  • b[2][1] = b[2][0] + 1 = 0 + 1 = 1 (horizontal)
  • c[2][1] = c[1][0] + 1 = 0 + 1 = 1 (diagonal)
  • d[2][1] = d[1][2] + 1 = 1 + 1 = 2 (anti-diagonal - extends from top-right!)
  • Current max = 2

Position (1,1) - mat value is 1:

  • a[2][2] = a[1][2] + 1 = 1 + 1 = 2 (vertical - extends from above!)
  • b[2][2] = b[2][1] + 1 = 1 + 1 = 2 (horizontal - extends from left!)
  • c[2][2] = c[1][1] + 1 = 0 + 1 = 1 (diagonal)
  • d[2][2] = d[1][3] + 1 = 1 + 1 = 2 (anti-diagonal - extends!)
  • Current max = 2

Position (1,2) - mat value is 0:

  • All values remain 0 at position [2][3]
  • Current max = 2

Position (2,0) - mat value is 1:

  • a[3][1] = a[2][1] + 1 = 1 + 1 = 2 (vertical - extends from above!)
  • b[3][1] = b[3][0] + 1 = 0 + 1 = 1 (horizontal)
  • c[3][1] = c[2][0] + 1 = 0 + 1 = 1 (diagonal)
  • d[3][1] = d[2][2] + 1 = 2 + 1 = 3 (anti-diagonal - extends further!)
  • Current max = 3

Final Answer: 3

The longest line is the anti-diagonal from position (0,1) → (1,0) → (2,0), which has length 3.

This example demonstrates how:

  • Each direction tracks independently
  • Counts extend when consecutive 1s are found
  • The anti-diagonal direction found the longest line by building up counts: 1 → 2 → 3

Solution Implementation

1class Solution:
2    def longestLine(self, mat: List[List[int]]) -> int:
3        # Get matrix dimensions
4        rows, cols = len(mat), len(mat[0])
5      
6        # Initialize DP tables for each direction with padding to avoid boundary checks
7        # Adding 2 to dimensions: 1 for 1-indexed access, 1 for boundary padding
8        vertical = [[0] * (cols + 2) for _ in range(rows + 2)]
9        horizontal = [[0] * (cols + 2) for _ in range(rows + 2)]
10        diagonal = [[0] * (cols + 2) for _ in range(rows + 2)]
11        anti_diagonal = [[0] * (cols + 2) for _ in range(rows + 2)]
12      
13        # Track the maximum line length found
14        max_length = 0
15      
16        # Iterate through each cell in the matrix
17        for i in range(1, rows + 1):
18            for j in range(1, cols + 1):
19                # Check if current cell contains a 1
20                if mat[i - 1][j - 1] == 1:
21                    # Update consecutive count for vertical line (from top)
22                    vertical[i][j] = vertical[i - 1][j] + 1
23                  
24                    # Update consecutive count for horizontal line (from left)
25                    horizontal[i][j] = horizontal[i][j - 1] + 1
26                  
27                    # Update consecutive count for diagonal line (from top-left)
28                    diagonal[i][j] = diagonal[i - 1][j - 1] + 1
29                  
30                    # Update consecutive count for anti-diagonal line (from top-right)
31                    anti_diagonal[i][j] = anti_diagonal[i - 1][j + 1] + 1
32                  
33                    # Update maximum length across all four directions
34                    max_length = max(max_length, 
35                                   vertical[i][j], 
36                                   horizontal[i][j], 
37                                   diagonal[i][j], 
38                                   anti_diagonal[i][j])
39      
40        return max_length
41
1class Solution {
2    public int longestLine(int[][] mat) {
3        int rows = mat.length;
4        int cols = mat[0].length;
5      
6        // Create 2D arrays with padding to avoid boundary checks
7        // Each array tracks consecutive 1s in different directions
8        int[][] vertical = new int[rows + 2][cols + 2];    // Tracks vertical lines
9        int[][] horizontal = new int[rows + 2][cols + 2];  // Tracks horizontal lines
10        int[][] diagonal = new int[rows + 2][cols + 2];     // Tracks diagonal lines (top-left to bottom-right)
11        int[][] antiDiagonal = new int[rows + 2][cols + 2]; // Tracks anti-diagonal lines (top-right to bottom-left)
12      
13        int maxLength = 0;
14      
15        // Iterate through the matrix
16        for (int i = 1; i <= rows; i++) {
17            for (int j = 1; j <= cols; j++) {
18                // Check if current cell contains 1
19                if (mat[i - 1][j - 1] == 1) {
20                    // Update consecutive count for each direction
21                    vertical[i][j] = vertical[i - 1][j] + 1;           // Add to vertical line from above
22                    horizontal[i][j] = horizontal[i][j - 1] + 1;        // Add to horizontal line from left
23                    diagonal[i][j] = diagonal[i - 1][j - 1] + 1;        // Add to diagonal line from top-left
24                    antiDiagonal[i][j] = antiDiagonal[i - 1][j + 1] + 1; // Add to anti-diagonal line from top-right
25                  
26                    // Update maximum length found so far
27                    maxLength = max(maxLength, vertical[i][j], horizontal[i][j], 
28                                  diagonal[i][j], antiDiagonal[i][j]);
29                }
30            }
31        }
32      
33        return maxLength;
34    }
35  
36    /**
37     * Helper method to find the maximum value among given integers
38     * @param arr Variable number of integer arguments
39     * @return The maximum value among all arguments
40     */
41    private int max(int... arr) {
42        int maximum = 0;
43        for (int value : arr) {
44            maximum = Math.max(maximum, value);
45        }
46        return maximum;
47    }
48}
49
1class Solution {
2public:
3    int longestLine(vector<vector<int>>& mat) {
4        int rows = mat.size();
5        int cols = mat[0].size();
6      
7        // Create DP tables for four directions with padding to avoid boundary checks
8        // vertical[i][j]: length of consecutive 1s ending at (i,j) in vertical direction
9        vector<vector<int>> vertical(rows + 2, vector<int>(cols + 2));
10        // horizontal[i][j]: length of consecutive 1s ending at (i,j) in horizontal direction
11        vector<vector<int>> horizontal(rows + 2, vector<int>(cols + 2));
12        // diagonal[i][j]: length of consecutive 1s ending at (i,j) in diagonal direction (top-left to bottom-right)
13        vector<vector<int>> diagonal(rows + 2, vector<int>(cols + 2));
14        // antiDiagonal[i][j]: length of consecutive 1s ending at (i,j) in anti-diagonal direction (top-right to bottom-left)
15        vector<vector<int>> antiDiagonal(rows + 2, vector<int>(cols + 2));
16      
17        int maxLength = 0;
18      
19        // Iterate through each cell in the matrix
20        for (int i = 1; i <= rows; ++i) {
21            for (int j = 1; j <= cols; ++j) {
22                // Check if current cell in original matrix is 1
23                if (mat[i - 1][j - 1] == 1) {
24                    // Update consecutive 1s count for vertical direction (from top)
25                    vertical[i][j] = vertical[i - 1][j] + 1;
26                  
27                    // Update consecutive 1s count for horizontal direction (from left)
28                    horizontal[i][j] = horizontal[i][j - 1] + 1;
29                  
30                    // Update consecutive 1s count for diagonal direction (from top-left)
31                    diagonal[i][j] = diagonal[i - 1][j - 1] + 1;
32                  
33                    // Update consecutive 1s count for anti-diagonal direction (from top-right)
34                    antiDiagonal[i][j] = antiDiagonal[i - 1][j + 1] + 1;
35                  
36                    // Update the maximum length found so far
37                    maxLength = max(maxLength, 
38                                  max(vertical[i][j], 
39                                    max(horizontal[i][j], 
40                                      max(diagonal[i][j], antiDiagonal[i][j]))));
41                }
42            }
43        }
44      
45        return maxLength;
46    }
47};
48
1function longestLine(mat: number[][]): number {
2    const rows: number = mat.length;
3    const cols: number = mat[0].length;
4  
5    // Create DP tables for four directions with padding to avoid boundary checks
6    // vertical[i][j]: length of consecutive 1s ending at (i,j) in vertical direction
7    const vertical: number[][] = Array(rows + 2).fill(null).map(() => Array(cols + 2).fill(0));
8    // horizontal[i][j]: length of consecutive 1s ending at (i,j) in horizontal direction
9    const horizontal: number[][] = Array(rows + 2).fill(null).map(() => Array(cols + 2).fill(0));
10    // diagonal[i][j]: length of consecutive 1s ending at (i,j) in diagonal direction (top-left to bottom-right)
11    const diagonal: number[][] = Array(rows + 2).fill(null).map(() => Array(cols + 2).fill(0));
12    // antiDiagonal[i][j]: length of consecutive 1s ending at (i,j) in anti-diagonal direction (top-right to bottom-left)
13    const antiDiagonal: number[][] = Array(rows + 2).fill(null).map(() => Array(cols + 2).fill(0));
14  
15    let maxLength: number = 0;
16  
17    // Iterate through each cell in the matrix
18    for (let i = 1; i <= rows; i++) {
19        for (let j = 1; j <= cols; j++) {
20            // Check if current cell in original matrix is 1
21            if (mat[i - 1][j - 1] === 1) {
22                // Update consecutive 1s count for vertical direction (from top)
23                vertical[i][j] = vertical[i - 1][j] + 1;
24              
25                // Update consecutive 1s count for horizontal direction (from left)
26                horizontal[i][j] = horizontal[i][j - 1] + 1;
27              
28                // Update consecutive 1s count for diagonal direction (from top-left)
29                diagonal[i][j] = diagonal[i - 1][j - 1] + 1;
30              
31                // Update consecutive 1s count for anti-diagonal direction (from top-right)
32                antiDiagonal[i][j] = antiDiagonal[i - 1][j + 1] + 1;
33              
34                // Update the maximum length found so far
35                maxLength = Math.max(
36                    maxLength,
37                    Math.max(
38                        vertical[i][j],
39                        Math.max(
40                            horizontal[i][j],
41                            Math.max(diagonal[i][j], antiDiagonal[i][j])
42                        )
43                    )
44                );
45            }
46        }
47    }
48  
49    return maxLength;
50}
51

Time and Space Complexity

The time complexity is O(m × n), where m is the number of rows and n is the number of columns in the matrix. This is because the algorithm uses two nested loops that iterate through each cell of the matrix exactly once (from row 1 to m and column 1 to n), performing constant-time operations for each cell.

The space complexity is O(m × n), where m and n are the number of rows and columns in the matrix, respectively. The algorithm creates four 2D arrays (a, b, c, and d), each of size (m + 2) × (n + 2). Since each array requires O((m + 2) × (n + 2)) space, which simplifies to O(m × n), and we have four such arrays, the total space complexity remains O(m × n).

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

Common Pitfalls

1. Index Confusion with Padding

The most common mistake is mishandling the index mapping between the original matrix and the padded DP arrays. Since the DP arrays have dimensions (m+2) x (n+2) while the original matrix is m x n, developers often make off-by-one errors when accessing elements.

Incorrect Implementation:

# Wrong: Forgetting to adjust indices
if mat[i][j] == 1:  # This will cause IndexError
    vertical[i][j] = vertical[i-1][j] + 1

Correct Implementation:

# Correct: Properly adjusting indices for padded arrays
if mat[i-1][j-1] == 1:  # Subtract 1 from both indices
    vertical[i][j] = vertical[i-1][j] + 1

2. Anti-Diagonal Direction Error

A frequent mistake occurs when updating the anti-diagonal direction. Since anti-diagonals go from top-right to bottom-left, the column index should increase (not decrease) when looking at the previous position.

Incorrect Implementation:

# Wrong: Using j-1 instead of j+1 for anti-diagonal
anti_diagonal[i][j] = anti_diagonal[i-1][j-1] + 1  # This tracks diagonal, not anti-diagonal

Correct Implementation:

# Correct: Previous position for anti-diagonal is at (i-1, j+1)
anti_diagonal[i][j] = anti_diagonal[i-1][j+1] + 1

3. Space Optimization Trap

Some developers attempt to optimize space by reusing arrays or using a single 3D array, but this can lead to incorrect results if not carefully implemented.

Problematic Optimization:

# Attempting to use a single array for multiple directions
dp = [[0] * (cols + 2) for _ in range(rows + 2)]
# This won't work because each direction needs independent tracking

Better Approach (if space optimization is needed):

# Use a 3D array with proper indexing
dp = [[[0] * 4 for _ in range(cols + 2)] for _ in range(rows + 2)]
# dp[i][j][0] for vertical, dp[i][j][1] for horizontal, etc.

4. Forgetting to Initialize Maximum Value

Starting with an uninitialized or incorrectly initialized maximum value can lead to wrong results, especially for empty matrices or matrices with all zeros.

Incorrect Implementation:

# Wrong: Not initializing or using wrong initial value
max_length = float('-inf')  # Problematic for all-zero matrices

Correct Implementation:

# Correct: Initialize to 0 to handle edge cases properly
max_length = 0

5. Boundary Checking Without Padding

If attempting to solve without padding, forgetting boundary checks leads to index out of bounds errors.

Alternative Solution Without Padding (requires careful boundary checking):

for i in range(rows):
    for j in range(cols):
        if mat[i][j] == 1:
            # Must check boundaries before accessing previous cells
            vertical[i][j] = (vertical[i-1][j] if i > 0 else 0) + 1
            horizontal[i][j] = (horizontal[i][j-1] if j > 0 else 0) + 1
            diagonal[i][j] = (diagonal[i-1][j-1] if i > 0 and j > 0 else 0) + 1
            anti_diagonal[i][j] = (anti_diagonal[i-1][j+1] if i > 0 and j < cols-1 else 0) + 1

The padding approach in the original solution elegantly avoids these boundary checks, making the code cleaner and less error-prone.

Discover Your Strengths and Weaknesses: Take Our 3-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