Border Subgrid Count II
Problem Overview
| Attribute | Value |
|---|---|
| Difficulty | Hard |
| Category | Grid Counting / 2D Prefix Sums |
| Time Limit | 1.0 seconds |
| Key Technique | 2D Prefix Sum, Range Queries |
Learning Goals
After solving this problem, you will be able to:
- Distinguish between border cells and interior cells in a subgrid
- Build and query 2D prefix sums for multiple value types
- Apply early termination to optimize brute force validation
- Recognize when prefix sums convert O(hw) validation to O(1)
Problem Statement
Problem: Given an n x m grid, count all subgrids where border cells equal a specific value and interior cells equal a different value.
Input:
- Line 1: Two integers n and m (grid dimensions)
- Lines 2 to n+1: m integers per row representing the grid
Output:
- A single integer: the count of valid border subgrids
Constraints:
- 1 <= n, m <= 1000
- Grid values are integers (typically 0 or 1)
Example
Input:
4 4
1 1 1 1
1 0 0 1
1 0 0 1
1 1 1 1
Output:
1
Explanation: Only the 4x4 subgrid satisfies the condition:
- Border cells (perimeter): all 1s
- Interior cells (2x2 center): all 0s
Intuition: How to Think About This Problem
Pattern Recognition
Key Question: How do we efficiently verify both border AND interior conditions for every possible subgrid?
The challenge is that a naive check of every cell in every subgrid is O(n^2 * m^2 * n * m). We need to validate border and interior regions in O(1) time using prefix sums.
Breaking Down the Problem
- What are we counting? Subgrids where border = value_b and interior = value_i
- What defines a border? First/last row and first/last column of the subgrid
- What defines interior? All cells not on the border (requires height >= 3 and width >= 3)
Visual Understanding
Subgrid structure (4x5):
+---+---+---+---+---+
| B | B | B | B | B | <- Top border
+---+---+---+---+---+
| B | I | I | I | B | <- Interior with side borders
+---+---+---+---+---+
| B | I | I | I | B | <- Interior with side borders
+---+---+---+---+---+
| B | B | B | B | B | <- Bottom border
+---+---+---+---+---+
B = Border cell (must equal border_value)
I = Interior cell (must equal interior_value)
Border count = 2*(h + w) - 4 = 2*(4 + 5) - 4 = 14
Interior count = (h-2)*(w-2) = 2*3 = 6
Solution 1: Brute Force
Idea
Check every possible subgrid by iterating through all cells and validating border/interior conditions.
Algorithm
- Enumerate all top-left corners (i, j)
- Enumerate all heights and widths
- For each subgrid, check all border and interior cells
Code
def count_border_subgrids_brute(n, m, grid, border_val=1, interior_val=0):
"""
Brute force: check all subgrids by examining each cell.
Time: O(n^2 * m^2 * n * m) worst case
Space: O(1)
"""
count = 0
for i in range(n):
for j in range(m):
for h in range(1, n - i + 1):
for w in range(1, m - j + 1):
if is_valid_subgrid(grid, i, j, h, w, border_val, interior_val):
count += 1
return count
def is_valid_subgrid(grid, r, c, h, w, border_val, interior_val):
"""Check if subgrid has valid border and interior."""
for i in range(r, r + h):
for j in range(c, c + w):
is_border = (i == r or i == r + h - 1 or j == c or j == c + w - 1)
if is_border:
if grid[i][j] != border_val:
return False
else:
if grid[i][j] != interior_val:
return False
return True
Complexity
| Metric | Value | Explanation |
|---|---|---|
| Time | O(n^4 * m^2) | Four nested loops + O(nm) validation |
| Space | O(1) | No extra space |
Why This Is Slow
Each subgrid validation scans up to n*m cells. With O(n^2 * m^2) subgrids, this becomes impractical for n, m = 1000.
Solution 2: Optimal - 2D Prefix Sums
Key Insight
The Trick: Build two prefix sum arrays - one for border values, one for interior values. Then validate any rectangular region in O(1).
Prefix Sum Concept
A 2D prefix sum allows us to compute the sum of any rectangle in O(1):
sum(r1, c1, r2, c2) = prefix[r2+1][c2+1] - prefix[r1][c2+1]
- prefix[r2+1][c1] + prefix[r1][c1]
Algorithm
- Build prefix sum for cells matching
border_val - Build prefix sum for cells matching
interior_val - For each subgrid, use prefix sums to count matching cells
- Compare counts against expected border/interior sizes
Dry Run Example
Grid (4x4): border_val = 1, interior_val = 0
1 1 1 1
1 0 0 1
1 0 0 1
1 1 1 1
Check subgrid from (0,0) with height=4, width=4:
Step 1: Calculate expected counts
Border cells = 2*(4+4) - 4 = 12
Interior cells = (4-2)*(4-2) = 4
Step 2: Query prefix sums
Total 1s in entire grid = 12 (using border_prefix)
Total 0s in interior region (1,1) to (2,2) = 4 (using interior_prefix)
Step 3: Validate
All 12 border positions have value 1? YES (12 == 12)
All 4 interior positions have value 0? YES (4 == 4)
Result: Valid subgrid, count++
Code
def count_border_subgrids_optimal(n, m, grid, border_val=1, interior_val=0):
"""
Optimal solution using 2D prefix sums.
Time: O(n^2 * m^2) for enumeration, O(1) per validation
Space: O(n * m) for prefix sum arrays
"""
# Build prefix sums
border_ps = build_prefix_sum(n, m, grid, border_val)
interior_ps = build_prefix_sum(n, m, grid, interior_val)
count = 0
for i in range(n):
for j in range(m):
for h in range(1, n - i + 1):
for w in range(1, m - j + 1):
if is_valid_with_prefix(border_ps, interior_ps,
i, j, h, w):
count += 1
return count
def build_prefix_sum(n, m, grid, target):
"""Build 2D prefix sum for cells equal to target."""
ps = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(n):
for j in range(m):
ps[i+1][j+1] = (ps[i][j+1] + ps[i+1][j] - ps[i][j]
+ (1 if grid[i][j] == target else 0))
return ps
def query_sum(ps, r1, c1, r2, c2):
"""Query rectangle sum using prefix sums."""
return ps[r2+1][c2+1] - ps[r1][c2+1] - ps[r2+1][c1] + ps[r1][c1]
def is_valid_with_prefix(border_ps, interior_ps, r, c, h, w):
"""Validate subgrid using prefix sums."""
# Total border_val count in entire subgrid
total_border = query_sum(border_ps, r, c, r + h - 1, c + w - 1)
# Small subgrids (no interior) - all cells are border
if h < 3 or w < 3:
expected = h * w
return total_border == expected
# Calculate expected border cells
expected_border = 2 * (h + w) - 4
# Check interior region
interior_count = query_sum(interior_ps, r + 1, c + 1, r + h - 2, c + w - 2)
expected_interior = (h - 2) * (w - 2)
return total_border == expected_border + interior_count and \
interior_count == expected_interior
Complexity
| Metric | Value | Explanation |
|---|---|---|
| Time | O(n^2 * m^2) | Enumerate all subgrids, O(1) validation each |
| Space | O(n * m) | Two prefix sum arrays |
Common Mistakes
Mistake 1: Wrong Interior Region Indices
# WRONG - off by one error
interior = query_sum(ps, r, c, r + h - 1, c + w - 1) # This is entire grid!
# CORRECT - interior starts at (r+1, c+1) and ends at (r+h-2, c+w-2)
interior = query_sum(ps, r + 1, c + 1, r + h - 2, c + w - 2)
Problem: Interior region excludes the border, not the entire subgrid. Fix: Interior spans from (r+1, c+1) to (r+h-2, c+w-2).
Mistake 2: Forgetting Small Subgrids Have No Interior
# WRONG - accessing invalid interior region
interior = query_sum(ps, r + 1, c + 1, r + h - 2, c + w - 2) # Invalid if h < 3
# CORRECT - check size first
if h < 3 or w < 3:
return all_cells_are_border
Problem: A 2x2 or 1xN subgrid has no interior cells. Fix: Handle small subgrids as special cases.
Mistake 3: Border Count Formula Error
# WRONG
border_count = 2 * h + 2 * w # Overcounts corners
# CORRECT
border_count = 2 * (h + w) - 4 # Subtract 4 corners counted twice
Edge Cases
| Case | Input | Expected | Why |
|---|---|---|---|
| 1x1 grid | [[1]] |
1 | Single cell is its own border |
| All same value | [[1,1],[1,1]] |
Count all | Every subgrid valid if border=1, interior=1 |
| No valid subgrids | [[0,0],[0,0]] |
0 | No border_val=1 exists |
| Minimum with interior | 3x3 | Check | Smallest grid with 1 interior cell |
| Large grid | 1000x1000 | Varies | Test performance |
When to Use This Pattern
Use 2D Prefix Sums When:
- Counting cells with specific values in rectangular regions
- Multiple queries over the same grid
- O(1) range sum is critical for performance
Alternative Approaches:
- Early termination brute force: When most subgrids fail quickly
- Sparse grids: When target values are rare, enumerate positions instead
Pattern Recognition Checklist:
- Need to sum/count values in rectangles? → 2D Prefix Sum
- Multiple conditions on different regions? → Multiple prefix arrays
- Need O(1) validation per subgrid? → Prefix sum queries
Related Problems
CSES Problems
| Problem | Technique |
|---|---|
| Forest Queries | Basic 2D prefix sum |
| Subarray Sums I | 1D prefix sum + hash map |
Similar Difficulty
| Problem | Key Difference |
|---|---|
| LeetCode: Maximal Square | DP instead of counting |
| LeetCode: Range Sum Query 2D | Pure prefix sum queries |
| LeetCode: Number of Submatrices That Sum to Target | Prefix sum + hash map |
Key Takeaways
- Core Idea: Use separate prefix sums for border and interior values to validate in O(1)
- Time Optimization: Reduce validation from O(hw) to O(1) per subgrid
- Space Trade-off: O(nm) space enables O(1) queries
- Border Formula: Border cells = 2(h + w) - 4, Interior = (h-2)(w-2)
Practice Checklist
Before moving on, make sure you can:
- Build a 2D prefix sum array from scratch
- Query any rectangular region in O(1)
- Correctly identify border vs interior cells
- Handle edge cases (small subgrids, empty interiors)
Additional Resources
- CP-Algorithms: 2D Prefix Sums
- CSES Forest Queries II - 2D range updates