Taro Logo

Count Cells in Overlapping Horizontal and Vertical Substrings

Medium
Asked by:
Profile picture
21 views
Topics:
ArraysStrings

You are given an m x n matrix grid consisting of characters and a string pattern.

A horizontal substring is a contiguous sequence of characters read from left to right. If the end of a row is reached before the substring is complete, it wraps to the first column of the next row and continues as needed. You do not wrap from the bottom row back to the top.

A vertical substring is a contiguous sequence of characters read from top to bottom. If the bottom of a column is reached before the substring is complete, it wraps to the first row of the next column and continues as needed. You do not wrap from the last column back to the first.

Count the number of cells in the matrix that satisfy the following condition:

  • The cell must be part of at least one horizontal substring and at least one vertical substring, where both substrings are equal to the given pattern.

Return the count of these cells.

Example 1:

Input: grid = [["a","a","c","c"],["b","b","b","c"],["a","a","b","a"],["c","a","a","c"],["a","a","b","a"]], pattern = "abaca"

Output: 1

Explanation:

The pattern "abaca" appears once as a horizontal substring (colored blue) and once as a vertical substring (colored red), intersecting at one cell (colored purple).

Example 2:

Input: grid = [["c","a","a","a"],["a","a","b","a"],["b","b","a","a"],["a","a","b","a"]], pattern = "aba"

Output: 4

Explanation:

The cells colored above are all part of at least one horizontal and one vertical substring matching the pattern "aba".

Example 3:

Input: grid = [["a"]], pattern = "a"

Output: 1

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 1000
  • 1 <= m * n <= 105
  • 1 <= pattern.length <= m * n
  • grid and pattern consist of only lowercase English letters.

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What are the dimensions (rows and columns) of the 2D grid, and what are the constraints on those dimensions (minimum and maximum values)?
  2. What data type will the grid contain (e.g., integers, booleans, characters), and what values are considered 'cells' for the purpose of counting (e.g., are cells only counted if they contain a '1' or are not 'empty')?
  3. Can the horizontal and vertical substrings overlap, and if so, how should overlapping cells be counted (once or multiple times)?
  4. Are the horizontal and vertical substrings guaranteed to exist within the grid boundaries, or do I need to handle cases where they might extend beyond the grid?
  5. If no cells are found in overlapping substrings, should I return 0, null, or throw an exception?

Brute Force Solution

Approach

The brute force approach to counting cells in overlapping substrings involves checking every possible horizontal and vertical substring combination. We meticulously examine each potential rectangular area formed by these substrings and count the cells covered. This approach guarantees finding the correct answer by exhaustive search, but it's not the most efficient way.

Here's how the algorithm would work step-by-step:

  1. Consider all possible starting positions for a horizontal substring within the grid.
  2. For each starting position, consider all possible lengths of horizontal substrings that can be formed.
  3. Repeat the same process for vertical substrings: explore all starting positions and lengths.
  4. Now, take one horizontal substring and one vertical substring.
  5. Determine if these two substrings overlap in any cells.
  6. If they overlap, count the number of overlapping cells.
  7. Repeat steps 4-6 for every single possible pairing of horizontal and vertical substrings.
  8. Sum up the counts of overlapping cells from all pairs to obtain the final result.

Code Implementation

def count_overlapping_cells_brute_force(grid):
    rows = len(grid)
    cols = len(grid[0]) if rows > 0 else 0
    overlapping_cells_count = 0

    # Iterate through all possible horizontal substrings
    for horizontal_start_row in range(rows):
        for horizontal_length in range(1, cols + 1):

            # Iterate through all possible vertical substrings
            for vertical_start_col in range(cols):
                for vertical_length in range(1, rows + 1):

                    overlap_exists = False
                    cells_in_overlap = 0

                    #Determine if substrings overlap
                    for row_index in range(vertical_start_col, min(vertical_start_col + vertical_length, rows)):
                        for col_index in range(horizontal_start_row, min(horizontal_start_row + horizontal_length, cols)):

                            if (vertical_start_col <= row_index < vertical_start_col + vertical_length) and \
                               (horizontal_start_row <= col_index < horizontal_start_row + horizontal_length): #check intersection
                                cells_in_overlap += 1
                                overlap_exists = True

                    if overlap_exists:
                        overlapping_cells_count += cells_in_overlap

    return overlapping_cells_count

Big(O) Analysis

Time Complexity
O(n^6)The algorithm iterates through all possible horizontal substrings, which takes O(n^2) time (O(n) for starting position and O(n) for length). Similarly, it iterates through all possible vertical substrings, also taking O(n^2) time. For each pair of horizontal and vertical substrings, it checks for overlaps, which takes O(n^2) time because the overlap check requires iterating through all possible cells within the grid of size n x n. Since we have O(n^2) horizontal substrings and O(n^2) vertical substrings, we perform the O(n^2) overlap check O(n^2) * O(n^2) times. Therefore, the overall time complexity is O(n^2) * O(n^2) * O(n^2) = O(n^6).
Space Complexity
O(1)The described brute force approach primarily involves iterating through possible substring combinations using loops. It does not explicitly mention creating any auxiliary data structures like arrays, lists, or hash maps to store intermediate results. Therefore, the space complexity is dominated by a few counter variables or index variables for loop iterations. These variables take up a constant amount of space regardless of the grid size, which we can denote as N. Consequently, the space complexity remains constant, or O(1).

Optimal Solution

Approach

The efficient strategy focuses on tracking which cells are covered by horizontal and vertical 'beams' of coverage. The key idea is to only count cells covered by both a horizontal and a vertical beam once.

Here's how the algorithm would work step-by-step:

  1. Imagine the grid as a coordinate system, where each cell has a row and column number.
  2. Keep track of which rows have horizontal coverage by marking them as covered.
  3. Similarly, keep track of which columns have vertical coverage by marking them as covered.
  4. Go through each cell in the grid.
  5. For each cell, check if its row is horizontally covered AND its column is vertically covered.
  6. If both are covered, increase the count of cells covered by both.
  7. The final count is the answer.

Code Implementation

def count_overlapping_cells(grid, horizontal_substrings, vertical_substrings):
    number_of_rows = len(grid)
    number_of_columns = len(grid[0]) if number_of_rows > 0 else 0

    horizontally_covered_rows = [False] * number_of_rows
    vertically_covered_columns = [False] * number_of_columns

    for start_row, start_col, end_row, end_col in horizontal_substrings:
        # Mark rows as covered by horizontal substrings.
        for row_index in range(start_row, end_row + 1):
            horizontally_covered_rows[row_index] = True

    for start_row, start_col, end_row, end_col in vertical_substrings:
        # Mark cols as covered by vertical substrings.
        for col_index in range(start_col, end_col + 1):
            vertically_covered_columns[col_index] = True

    overlapping_cell_count = 0
    for row_index in range(number_of_rows):
        for col_index in range(number_of_columns):
            # Count cells covered by both horizontal and vertical substrings.
            if horizontally_covered_rows[row_index] and vertically_covered_columns[col_index]:
                overlapping_cell_count += 1

    return overlapping_cell_count

Big(O) Analysis

Time Complexity
O(n*m)Let n be the number of rows and m be the number of columns in the grid. The algorithm iterates through each cell in the grid to check for horizontal and vertical coverage. This single iteration visits n * m cells. Checking the covered rows and columns takes constant time for each cell. Therefore, the overall time complexity is O(n*m).
Space Complexity
O(N)The algorithm uses two data structures to keep track of covered rows and columns. These are boolean arrays or sets representing horizontal and vertical coverage. In the worst-case scenario, all rows and all columns can be covered which would be the number of rows + number of columns. The problem statement does not provide specific dimensions, but let's assume we have a grid of N rows and N columns. This leads to auxiliary arrays of size N each, summing up to 2N which simplifies to O(N).

Edge Cases

Null or empty matrix
How to Handle:
Return 0 if the matrix is null or has zero rows or columns.
Matrix with only one row or one column
How to Handle:
If only one row or column exists, there's no overlap, so return 0.
Matrix with only one cell
How to Handle:
Return 0 if the matrix contains only one element, because there are no substrings to compare.
Large matrix dimensions leading to potential integer overflow in count
How to Handle:
Use a 64-bit integer type (long in Java, int64_t in C++) for the count to avoid overflow.
All cells in the matrix have the same value
How to Handle:
If all values are the same, every row and column will match at every position; handle this case by carefully calculating the number of overlapping cells.
No overlapping substrings exist at all
How to Handle:
The algorithm should correctly return 0 when no overlapping cells are found.
Matrix with very long rows or columns exceeding memory limits
How to Handle:
If memory limits are a concern, consider processing the matrix in chunks or using a streaming approach.
Negative or zero values within the matrix
How to Handle:
The problem statement should clarify whether negative or zero values are allowed; if so, the solution should handle them correctly as cells to compare, otherwise filter them out.