3212. Count Submatrices With Equal Frequency of X and Y
Problem Description
You are given a 2D character matrix grid
where each cell grid[i][j]
contains one of three possible values: 'X'
, 'Y'
, or '.'
.
Your task is to count how many submatrices satisfy ALL of the following conditions:
- The submatrix must include the top-left corner of the grid (position
grid[0][0]
) - The submatrix must contain an equal number of
'X'
and'Y'
characters - The submatrix must contain at least one
'X'
character
A submatrix is defined by its top-left corner at (0, 0)
and any bottom-right corner at position (i, j)
where 0 ≤ i < m
and 0 ≤ j < n
(where m
and n
are the dimensions of the grid).
For example, if you have a 3×3 grid, possible submatrices that start at (0, 0)
could end at positions like (0, 0)
, (0, 1)
, (1, 1)
, (2, 2)
, etc. Each such submatrix is checked to see if it meets the three conditions above.
The dot character '.'
is neutral and doesn't count as either 'X'
or 'Y'
.
Return the total number of submatrices that satisfy all the conditions.
Intuition
Since we need to check all submatrices that start at position (0, 0)
, we essentially need to examine every possible bottom-right corner position (i, j)
in the grid. For each such position, we need to quickly determine:
- How many
'X'
characters are in the submatrix from(0, 0)
to(i, j)
- How many
'Y'
characters are in the submatrix from(0, 0)
to(i, j)
If we calculate this naively for each position, we'd have to traverse the entire submatrix every time, which would be inefficient.
The key insight is to use 2D prefix sums. This technique allows us to precompute cumulative counts as we traverse the grid, so we can instantly know the count of 'X'
and 'Y'
characters in any submatrix starting from (0, 0)
.
For each position (i, j)
, we can calculate the prefix sum using the formula:
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + current_cell_value
The subtraction of s[i-1][j-1]
is necessary because when we add s[i-1][j]
and s[i][j-1]
, we're counting the rectangle from (0, 0)
to (i-1, j-1)
twice.
We maintain two separate prefix sums: one for counting 'X'
characters and another for counting 'Y'
characters. As we build these prefix sums for each position, we can immediately check if:
- The count of
'X'
is greater than 0 (at least one'X'
exists) - The count of
'X'
equals the count of'Y'
(equal frequency)
If both conditions are met, we've found a valid submatrix and increment our answer.
This approach is efficient because we only traverse the grid once, and at each position, we can determine in constant time whether the submatrix from (0, 0)
to that position is valid.
Learn more about Prefix Sum patterns.
Solution Approach
We implement the solution using a 2D prefix sum approach. Here's how the algorithm works:
Data Structure Setup:
We create a 3D array s
with dimensions (m+1) × (n+1) × 2
, where:
s[i][j][0]
stores the count of'X'
characters in the submatrix from(0, 0)
to(i-1, j-1)
s[i][j][1]
stores the count of'Y'
characters in the submatrix from(0, 0)
to(i-1, j-1)
- We use 1-indexed arrays (hence
m+1
andn+1
) to avoid boundary checks
Algorithm Steps:
-
Initialize the prefix sum array with zeros and set answer counter
ans = 0
-
Traverse the grid row by row, and for each cell at position
(i, j)
:a. Calculate prefix sums using the inclusion-exclusion principle:
s[i][j][0] = s[i-1][j][0] + s[i][j-1][0] - s[i-1][j-1][0] s[i][j][1] = s[i-1][j][1] + s[i][j-1][1] - s[i-1][j-1][1]
b. Update counts based on the current cell:
- If the cell contains
'X'
or'Y'
, increment the corresponding counter - The clever part:
ord(x) & 1
gives us0
for'X'
(ASCII 88) and1
for'Y'
(ASCII 89) - If the cell is
'.'
, we skip updating (it doesn't contribute to either count)
c. Check validity of the submatrix from
(0, 0)
to current position:- If
s[i][j][0] > 0
(at least one'X'
exists) AND - If
s[i][j][0] == s[i][j][1]
(equal frequency of'X'
and'Y'
) - Then increment the answer counter
- If the cell contains
-
Return the final count of valid submatrices
Time Complexity: O(m × n)
where m
and n
are the dimensions of the grid, as we visit each cell exactly once.
Space Complexity: O(m × n)
for storing the prefix sum array.
The beauty of this solution is that it computes everything in a single pass through the grid, making it highly efficient compared to checking every possible submatrix individually.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider this 3×3 grid:
X Y . Y X Y . Y X
We'll build our prefix sum array s
with dimensions 4×4×2 (using 1-indexing). Initially, all values are 0.
Step-by-step traversal:
Position (0,0) - Character 'X':
- Calculate prefix sums:
s[1][1][0] = 0 + 0 - 0 = 0
,s[1][1][1] = 0 + 0 - 0 = 0
- Current cell is 'X', so increment
s[1][1][0]
by 1 →s[1][1][0] = 1
- Check validity:
s[1][1][0] = 1 > 0
✓ ands[1][1][0] = 1 ≠ s[1][1][1] = 0
✗ - No valid submatrix yet
Position (0,1) - Character 'Y':
- Calculate prefix sums:
s[1][2][0] = 0 + 1 - 0 = 1
,s[1][2][1] = 0 + 0 - 0 = 0
- Current cell is 'Y', so increment
s[1][2][1]
by 1 →s[1][2][1] = 1
- Check validity:
s[1][2][0] = 1 > 0
✓ ands[1][2][0] = 1 == s[1][2][1] = 1
✓ - Found valid submatrix!
ans = 1
Position (0,2) - Character '.':
- Calculate prefix sums:
s[1][3][0] = 0 + 1 - 0 = 1
,s[1][3][1] = 0 + 1 - 0 = 1
- Current cell is '.', so no increment
- Check validity:
s[1][3][0] = 1 > 0
✓ ands[1][3][0] = 1 == s[1][3][1] = 1
✓ - Found valid submatrix!
ans = 2
Position (1,0) - Character 'Y':
- Calculate prefix sums:
s[2][1][0] = 1 + 0 - 0 = 1
,s[2][1][1] = 0 + 0 - 0 = 0
- Current cell is 'Y', so increment
s[2][1][1]
by 1 →s[2][1][1] = 1
- Check validity:
s[2][1][0] = 1 > 0
✓ ands[2][1][0] = 1 == s[2][1][1] = 1
✓ - Found valid submatrix!
ans = 3
Position (1,1) - Character 'X':
- Calculate prefix sums:
s[2][2][0] = 1 + 1 - 0 = 2
,s[2][2][1] = 1 + 1 - 0 = 2
- Current cell is 'X', so increment
s[2][2][0]
by 1 →s[2][2][0] = 3
- Check validity:
s[2][2][0] = 3 > 0
✓ ands[2][2][0] = 3 ≠ s[2][2][1] = 2
✗ - No valid submatrix
Position (1,2) - Character 'Y':
- Calculate prefix sums:
s[2][3][0] = 1 + 3 - 1 = 3
,s[2][3][1] = 1 + 2 - 1 = 2
- Current cell is 'Y', so increment
s[2][3][1]
by 1 →s[2][3][1] = 3
- Check validity:
s[2][3][0] = 3 > 0
✓ ands[2][3][0] = 3 == s[2][3][1] = 3
✓ - Found valid submatrix!
ans = 4
Continuing this process for the remaining positions (2,0), (2,1), and (2,2), we find that:
- Position (2,0): Not valid (3 X's, 2 Y's)
- Position (2,1): Not valid (4 X's, 3 Y's)
- Position (2,2): Valid (4 X's, 4 Y's) →
ans = 5
Final answer: 5 valid submatrices
The valid submatrices are those ending at positions: (0,1), (0,2), (1,0), (1,2), and (2,2).
Solution Implementation
1class Solution:
2 def numberOfSubmatrices(self, grid: List[List[str]]) -> int:
3 # Get grid dimensions
4 rows, cols = len(grid), len(grid[0])
5
6 # Initialize prefix sum array with padding
7 # prefix_sum[i][j][0] stores count of first type characters (e.g., 'Y')
8 # prefix_sum[i][j][1] stores count of second type characters (e.g., 'X')
9 prefix_sum = [[[0] * 2 for _ in range(cols + 1)] for _ in range(rows + 1)]
10
11 result_count = 0
12
13 # Iterate through each cell of the grid (1-indexed for easier prefix sum calculation)
14 for row_idx, row_data in enumerate(grid, 1):
15 for col_idx, cell_value in enumerate(row_data, 1):
16 # Calculate prefix sum for both character types using inclusion-exclusion principle
17 # Copy counts from top and left cells, subtract overlap
18 prefix_sum[row_idx][col_idx][0] = (prefix_sum[row_idx - 1][col_idx][0] +
19 prefix_sum[row_idx][col_idx - 1][0] -
20 prefix_sum[row_idx - 1][col_idx - 1][0])
21
22 prefix_sum[row_idx][col_idx][1] = (prefix_sum[row_idx - 1][col_idx][1] +
23 prefix_sum[row_idx][col_idx - 1][1] -
24 prefix_sum[row_idx - 1][col_idx - 1][1])
25
26 # If current cell is not empty, increment the appropriate counter
27 if cell_value != ".":
28 # Use ASCII value's least significant bit to determine character type
29 # 'X' (ASCII 88) & 1 = 0, 'Y' (ASCII 89) & 1 = 1
30 character_type = ord(cell_value) & 1
31 prefix_sum[row_idx][col_idx][character_type] += 1
32
33 # Check if submatrix from (0,0) to current position is valid
34 # Valid means: has at least one 'X' and equal counts of both types
35 if (prefix_sum[row_idx][col_idx][0] > 0 and
36 prefix_sum[row_idx][col_idx][0] == prefix_sum[row_idx][col_idx][1]):
37 result_count += 1
38
39 return result_count
40
1class Solution {
2 public int numberOfSubmatrices(char[][] grid) {
3 int rows = grid.length;
4 int cols = grid[0].length;
5
6 // Create a 3D prefix sum array
7 // prefixSum[i][j][0] stores count of 'X' from (0,0) to (i-1,j-1)
8 // prefixSum[i][j][1] stores count of 'Y' from (0,0) to (i-1,j-1)
9 int[][][] prefixSum = new int[rows + 1][cols + 1][2];
10
11 int result = 0;
12
13 // Build prefix sum array and count valid submatrices
14 for (int i = 1; i <= rows; i++) {
15 for (int j = 1; j <= cols; j++) {
16 // Calculate prefix sum for 'X' count using inclusion-exclusion principle
17 prefixSum[i][j][0] = prefixSum[i - 1][j][0]
18 + prefixSum[i][j - 1][0]
19 - prefixSum[i - 1][j - 1][0]
20 + (grid[i - 1][j - 1] == 'X' ? 1 : 0);
21
22 // Calculate prefix sum for 'Y' count using inclusion-exclusion principle
23 prefixSum[i][j][1] = prefixSum[i - 1][j][1]
24 + prefixSum[i][j - 1][1]
25 - prefixSum[i - 1][j - 1][1]
26 + (grid[i - 1][j - 1] == 'Y' ? 1 : 0);
27
28 // Check if submatrix from (0,0) to (i-1,j-1) is valid
29 // Valid means: contains at least one 'X' and equal number of 'X' and 'Y'
30 if (prefixSum[i][j][0] > 0 && prefixSum[i][j][0] == prefixSum[i][j][1]) {
31 result++;
32 }
33 }
34 }
35
36 return result;
37 }
38}
39
1class Solution {
2public:
3 int numberOfSubmatrices(vector<vector<char>>& grid) {
4 int rows = grid.size();
5 int cols = grid[0].size();
6
7 // Create prefix sum array with extra padding for easier calculation
8 // prefixSum[i][j][0] stores count of 'X' from (0,0) to (i-1,j-1)
9 // prefixSum[i][j][1] stores count of 'Y' from (0,0) to (i-1,j-1)
10 vector<vector<vector<int>>> prefixSum(rows + 1,
11 vector<vector<int>>(cols + 1,
12 vector<int>(2, 0)));
13
14 int result = 0;
15
16 // Build prefix sum array and count valid submatrices
17 for (int i = 1; i <= rows; ++i) {
18 for (int j = 1; j <= cols; ++j) {
19 // Calculate prefix sum for 'X' count using inclusion-exclusion principle
20 prefixSum[i][j][0] = prefixSum[i - 1][j][0]
21 + prefixSum[i][j - 1][0]
22 - prefixSum[i - 1][j - 1][0]
23 + (grid[i - 1][j - 1] == 'X' ? 1 : 0);
24
25 // Calculate prefix sum for 'Y' count using inclusion-exclusion principle
26 prefixSum[i][j][1] = prefixSum[i - 1][j][1]
27 + prefixSum[i][j - 1][1]
28 - prefixSum[i - 1][j - 1][1]
29 + (grid[i - 1][j - 1] == 'Y' ? 1 : 0);
30
31 // Check if current submatrix from (0,0) to (i-1,j-1) is valid
32 // Valid means: contains at least one 'X' and equal number of 'X' and 'Y'
33 if (prefixSum[i][j][0] > 0 && prefixSum[i][j][0] == prefixSum[i][j][1]) {
34 ++result;
35 }
36 }
37 }
38
39 return result;
40 }
41};
42
1/**
2 * Counts the number of submatrices starting from (0,0) that contain equal numbers of 'X' and 'Y',
3 * with at least one 'X' present.
4 *
5 * @param grid - 2D array of strings containing 'X', 'Y', or other characters
6 * @returns The number of valid submatrices
7 */
8function numberOfSubmatrices(grid: string[][]): number {
9 const rows: number = grid.length;
10 const cols: number = grid[0].length;
11
12 // Create a 2D prefix sum array where each cell stores [countX, countY]
13 // prefixSum[i][j] represents the count of 'X' and 'Y' in the submatrix from (0,0) to (i-1,j-1)
14 const prefixSum: number[][][] = Array.from(
15 { length: rows + 1 },
16 () => Array.from(
17 { length: cols + 1 },
18 () => [0, 0]
19 )
20 );
21
22 let validSubmatricesCount: number = 0;
23
24 // Build prefix sum array and count valid submatrices
25 for (let row = 1; row <= rows; row++) {
26 for (let col = 1; col <= cols; col++) {
27 // Calculate prefix sum for 'X' count using inclusion-exclusion principle
28 prefixSum[row][col][0] =
29 prefixSum[row - 1][col][0] + // Add count from top
30 prefixSum[row][col - 1][0] - // Add count from left
31 prefixSum[row - 1][col - 1][0] + // Subtract overlapping area
32 (grid[row - 1][col - 1] === 'X' ? 1 : 0); // Add current cell if 'X'
33
34 // Calculate prefix sum for 'Y' count using inclusion-exclusion principle
35 prefixSum[row][col][1] =
36 prefixSum[row - 1][col][1] + // Add count from top
37 prefixSum[row][col - 1][1] - // Add count from left
38 prefixSum[row - 1][col - 1][1] + // Subtract overlapping area
39 (grid[row - 1][col - 1] === 'Y' ? 1 : 0); // Add current cell if 'Y'
40
41 // Check if current submatrix from (0,0) to (row-1,col-1) is valid:
42 // - Must have at least one 'X' (countX > 0)
43 // - Must have equal number of 'X' and 'Y' (countX === countY)
44 if (prefixSum[row][col][0] > 0 && prefixSum[row][col][0] === prefixSum[row][col][1]) {
45 validSubmatricesCount++;
46 }
47 }
48 }
49
50 return validSubmatricesCount;
51}
52
Time and Space Complexity
Time Complexity: O(m × n)
The algorithm uses nested loops to iterate through the grid. The outer loop runs m
times (for each row), and the inner loop runs n
times (for each column). Within the nested loops, all operations are constant time O(1)
:
- Array access and assignment operations for the prefix sum array
s
- Arithmetic operations (addition and subtraction)
- Character comparison and bitwise operation
ord(x) & 1
- Conditional checks
Therefore, the overall time complexity is O(m × n)
.
Space Complexity: O(m × n)
The algorithm creates a 3D array s
with dimensions (m + 1) × (n + 1) × 2
. This array stores the prefix sums for tracking two different types of elements (determined by ord(x) & 1
). The total number of elements in this array is (m + 1) × (n + 1) × 2
, which is O(m × n)
in terms of space complexity.
The remaining variables (m
, n
, ans
, i
, j
, x
) use constant space O(1)
.
Therefore, the overall space complexity is O(m × n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Character Type Mapping
The code uses ord(cell_value) & 1
to determine character type, which maps 'X' to 0 and 'Y' to 1. However, the problem description and comments suggest checking for 'X' characters specifically (condition: "at least one 'X' character"). This creates confusion about which index represents which character.
The Issue:
- 'X' (ASCII 88) & 1 = 0, so 'X' counts are stored at index 0
- 'Y' (ASCII 89) & 1 = 1, so 'Y' counts are stored at index 1
- But the validity check uses
prefix_sum[row_idx][col_idx][0] > 0
to verify "at least one 'X'"
Solution: Make the mapping explicit and consistent:
# Clear mapping if cell_value == 'X': prefix_sum[row_idx][col_idx][0] += 1 elif cell_value == 'Y': prefix_sum[row_idx][col_idx][1] += 1 # Then check: at least one 'X' AND equal counts if (prefix_sum[row_idx][col_idx][0] > 0 and prefix_sum[row_idx][col_idx][0] == prefix_sum[row_idx][col_idx][1]): result_count += 1
2. Forgetting the Top-Left Corner Requirement
The current implementation correctly handles this by always computing submatrices from (0,0), but developers might mistakenly try to optimize by checking all possible submatrices, not just those starting at the top-left corner.
Wrong Approach:
# DON'T do this - checks ALL submatrices
for start_row in range(rows):
for start_col in range(cols):
for end_row in range(start_row, rows):
for end_col in range(start_col, cols):
# Check submatrix...
Correct Approach: Always start from (0,0) as the code currently does.
3. Off-by-One Errors with 1-Indexed Arrays
Using 1-indexed arrays for prefix sums can lead to indexing mistakes when accessing the original grid.
Potential Bug:
# Wrong: using row_idx/col_idx directly on grid if grid[row_idx][col_idx] != ".": # IndexError!
Correct Usage:
# Use enumerate with start=1 and access via loop variable
for row_idx, row_data in enumerate(grid, 1):
for col_idx, cell_value in enumerate(row_data, 1):
# cell_value is already the correct value from grid
4. Misunderstanding Empty Cells
Developers might incorrectly handle '.' characters by either counting them or excluding submatrices containing them entirely.
Wrong Interpretation:
# DON'T skip submatrices with dots if has_dot_in_submatrix: continue # Wrong! Dots are neutral
Correct Handling: Dots should be ignored in counting but not prevent a submatrix from being valid. The current code handles this correctly by simply not incrementing any counter when encountering '.'.
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!