LeetCode 3212 - Count Submatrices With Equal Frequency of X and Y
The problem asks us to count submatrices within a given 2D character matrix grid that satisfy three conditions. A submatrix is defined by a contiguous rectangle within the grid, and the submatrix must include the top-left cell grid[0][0].
Difficulty: 🟡 Medium
Topics: Array, Matrix, Prefix Sum
Solution
Problem Understanding
The problem asks us to count submatrices within a given 2D character matrix grid that satisfy three conditions. A submatrix is defined by a contiguous rectangle within the grid, and the submatrix must include the top-left cell grid[0][0]. The first condition is that the submatrix contains equal numbers of 'X' and 'Y'. The second condition is that there is at least one 'X' in the submatrix. The input grid can also contain the character '.', which represents a neutral cell that does not affect counts of 'X' or 'Y'.
The output is a single integer representing the number of valid submatrices that meet these criteria. Constraints indicate that the grid can be as large as 1000x1000, which rules out naive O(n^6) solutions that enumerate every possible submatrix and count characters individually.
Important edge cases include grids without any 'X' (output must be 0), grids without any 'Y' (output must be 0 unless equal counts of 'X' and 'Y' can exist), and grids where all cells are '.'. Another subtle point is that every valid submatrix must include grid[0][0], which restricts the set of submatrices significantly.
Approaches
Brute Force
The brute-force approach would enumerate all possible submatrices containing grid[0][0]. For each candidate submatrix, count the occurrences of 'X' and 'Y' and check whether they are equal and that there is at least one 'X'.
This is correct, as it checks every possible submatrix, but it is too slow because for an n x m grid, there are O(n^2 * m^2) submatrices. Counting 'X' and 'Y' in each submatrix requires another O(n * m) operation, yielding O(n^3 * m^3) overall, which is infeasible for n, m up to 1000.
Optimal Approach
The key insight is that we can transform the problem into a prefix sum problem using a value mapping for 'X' as +1, 'Y' as -1, and '.' as 0. The goal of having equal counts of 'X' and 'Y' is equivalent to finding submatrices whose summed values equal 0. The requirement of at least one 'X' can be enforced separately.
We can then reduce the 2D submatrix sum problem to a series of 1D subarray sum problems using prefix sums along rows and iterating over all possible column ranges. For each column range, we compute the running sum of the transformed values across rows and use a hashmap to count the number of contiguous row segments whose sum is 0, similar to the classic "subarray sum equals K" problem.
This drastically reduces complexity because it leverages cumulative sums and hashmaps to count valid submatrices in O(n * m^2) time rather than O(n^3 * m^3).
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^3 * m^3) | O(1) | Enumerate all submatrices and count 'X' and 'Y' |
| Optimal | O(n * m^2) | O(n) | Use prefix sums and hashmap to count zero-sum row ranges |
Algorithm Walkthrough
-
Transform the input grid to a numeric grid where
'X' = 1,'Y' = -1, and'.' = 0. This allows us to convert the equal count condition into a zero-sum problem. -
Compute prefix sums along rows to allow O(1) range sum queries for any column range.
-
Iterate over all pairs of columns
(left, right)representing the vertical boundaries of submatrices. For each pair: -
For each row, compute the cumulative sum of values in the column range
[left, right]. -
Track the running cumulative sum of these row sums and store counts in a hashmap.
-
Increment the answer whenever a zero cumulative sum is encountered in a contiguous segment, ensuring that the submatrix sum is zero.
-
For each candidate submatrix, verify that it contains at least one
'X'within the column range. This can be done by separately tracking'X'counts for each row segment. -
Return the total count of valid submatrices.
Why it works: Mapping 'X' to 1 and 'Y' to -1 reduces the equal count condition to a sum of zero, which can be efficiently detected using prefix sums and a hashmap. This ensures correctness while maintaining performance.
Python Solution
from typing import List
from collections import defaultdict
class Solution:
def numberOfSubmatrices(self, grid: List[List[str]]) -> int:
if not grid or not grid[0]:
return 0
n, m = len(grid), len(grid[0])
# Convert grid to numeric values: X=1, Y=-1, .=0
num_grid = [[1 if cell=='X' else -1 if cell=='Y' else 0 for cell in row] for row in grid]
x_grid = [[1 if cell=='X' else 0 for cell in row] for row in grid]
# Prefix sum for each row
for r in range(n):
for c in range(1, m):
num_grid[r][c] += num_grid[r][c-1]
x_grid[r][c] += x_grid[r][c-1]
ans = 0
for left in range(m):
for right in range(left, m):
cum_sum = 0
x_count = 0
counter = defaultdict(int)
counter[0] = 1
for r in range(n):
val = num_grid[r][right] - (num_grid[r][left-1] if left > 0 else 0)
x_val = x_grid[r][right] - (x_grid[r][left-1] if left > 0 else 0)
cum_sum += val
x_count += x_val
if x_count > 0:
ans += counter[cum_sum]
counter[cum_sum] += 1
return ans
This solution first maps the grid to numeric arrays for quick computation. Row-wise prefix sums allow efficient column range queries. For each column pair, a hashmap counts cumulative sums of row segments, and we ensure that submatrices contain at least one 'X'.
Go Solution
func numberOfSubmatrices(grid [][]byte) int {
n, m := len(grid), len(grid[0])
numGrid := make([][]int, n)
xGrid := make([][]int, n)
for i := 0; i < n; i++ {
numGrid[i] = make([]int, m)
xGrid[i] = make([]int, m)
for j := 0; j < m; j++ {
if grid[i][j] == 'X' {
numGrid[i][j] = 1
xGrid[i][j] = 1
} else if grid[i][j] == 'Y' {
numGrid[i][j] = -1
}
}
for j := 1; j < m; j++ {
numGrid[i][j] += numGrid[i][j-1]
xGrid[i][j] += xGrid[i][j-1]
}
}
ans := 0
for left := 0; left < m; left++ {
for right := left; right < m; right++ {
cumSum := 0
xCount := 0
counter := map[int]int{0: 1}
for r := 0; r < n; r++ {
val := numGrid[r][right]
xVal := xGrid[r][right]
if left > 0 {
val -= numGrid[r][left-1]
xVal -= xGrid[r][left-1]
}
cumSum += val
xCount += xVal
if xCount > 0 {
ans += counter[cumSum]
}
counter[cumSum]++
}
}
}
return ans
}
The Go implementation follows the same logic as Python, using slices instead of lists and maps for hash counting. It handles column prefix sums carefully and ensures proper initialization of the counter map.
Worked Examples
Example 1: grid = [["X","Y","."],["Y",".","."]]
Step through column ranges:
- left=0, right=0: row sums
[1, -1], cumulative sums[1,0], count of X in segments[1,1]→ valid segments detected. - left=0, right=1: row sums
[0, -1], cumulative sums[0,-1], count of X[1,1]→ valid segments detected. - left=0, right=2: row sums
[0, -1], cumulative sums[0,-1]→ valid segments. - Other column ranges yield no new valid submatrices.
Total count = 3.
Example 2: grid = `[["X","X"],["X","