Surrounded Regions
Problem
Given an m x n matrix board containing 'X' and 'O', capture all regions surrounded by 'X' by flipping all 'O's that are not connected to the border to 'X'.
- m == board.length
- n == board[i].length
- 1 ≤ m, n ≤ 200
- board[i][j] is 'X' or 'O'
Example
board = [['X','X','X','X'],['X','O','O','X'],['X','X','O','X'],['X','O','X','X']][['X','X','X','X'],['X','X','X','X'],['X','X','X','X'],['X','O','X','X']]The 'O's in the middle are surrounded by 'X' and flipped to 'X'. The 'O' at the bottom left corner is connected to the border, so it remains 'O'. The algorithm marks all 'O's connected to the border with a temporary marker 'T' using DFS, then flips all remaining 'O's to 'X', and finally restores 'T' back to 'O'.
Approach
Straightforward Solution
A naive approach would check each 'O' and perform a flood fill to determine if it is surrounded, which can be expensive and redundant, leading to O(m*n*(m+n)) time complexity.
Core Observation
Any 'O' connected to the border cannot be captured because it is not fully surrounded by 'X'. Therefore, the problem reduces to identifying and preserving all 'O's connected to the border.
Path to Optimal
PreviewThe key insight is to invert the problem: instead of searching for surrounded regions, start from the border and mark all 'O's connected to the border as safe. This can be done efficiently with DFS or BFS…
Full step-by-step walkthrough on Pro →
Optimal Approach
PreviewUse DFS starting from all border cells that contain 'O'. Mark these and all connected 'O's with a temporary marker 'T'…
Full step-by-step walkthrough on Pro →
Want the full reasoning chain?
Unlock the complete walkthrough, line-by-line analysis, and recall drill.
Unlock ProTime
O(m * n)
Each cell is visited at most once during the DFS marking and once during the final iteration to flip cells, resulting in linear time relative to the board size.
Space
O(m * n)
In the worst case, the recursion stack for DFS can grow up to O(m * n) if the entire board is filled with 'O's connected to the border. The in-place marking avoids extra data structures, but recursion stack space is required.
Pattern Spotlight
DFS (Boundary-Connected Region Marking)
When a problem requires identifying regions connected to boundaries, invert the problem by starting DFS from the boundary to mark safe regions, then process the rest accordingly.
Solution
| 1 | class Solution:
|
| 2 | def solve(self, board: list[list[str]]) -> None:
|
| 3 | ROWS, COLS = len(board), len(board[0])
|
| 4 |
|
| 5 | def capture(r, c):
|
| 6 | if r < 0 or c < 0 or r == ROWS or c == COLS or board[r][c] != "O":
|
| 7 | return
|
| 8 |
|
| 9 | board[r][c] = "T"
|
| 10 | capture(r + 1, c)
|
| 11 | capture(r - 1, c)
|
| 12 | capture(r, c + 1)
|
| 13 | capture(r, c - 1)
|
| 14 |
|
| 15 | for r in range(ROWS):
|
| 16 | for c in range(COLS):
|
| 17 | if board[r][c] == "O" and (r in [0, ROWS - 1] or c in [0, COLS - 1]):
|
| 18 | capture(r, c)
|
| 19 |
|
| 20 | for r in range(ROWS):
|
| 21 | for c in range(COLS):
|
| 22 | if board[r][c] == "O":
|
| 23 | board[r][c] = "X"
|
| 24 |
|
| 25 | for r in range(ROWS):
|
| 26 | for c in range(COLS):
|
| 27 | if board[r][c] == "T":
|
| 28 | board[r][c] = "O" |
Step-by-Step Solution
Mark Border-Connected 'O's Using DFS to Protect Safe Regions
| 5 | def capture(r, c):
|
| 6 | if r < 0 or c < 0 or r == ROWS or c == COLS or board[r][c] != "O":
|
| 7 | return
|
| 9 | board[r][c] = "T"
|
| 10 | capture(r + 1, c)
|
| 11 | capture(r - 1, c)
|
| 12 | capture(r, c + 1)
|
| 13 | capture(r, c - 1)
|
| 15 | for r in range(ROWS):
|
| 16 | for c in range(COLS):
|
| 17 | if board[r][c] == "O" and (r in [0, ROWS - 1] or c in [0, COLS - 1]):
|
| 18 | capture(r, c)
|
Objective
To identify and mark all 'O's connected to the border as safe by performing DFS from border cells.
Key Insight
Starting DFS from border 'O's ensures that all connected 'O's are marked as safe in a single pass. Marking them with a temporary character prevents revisiting and distinguishes safe 'O's from those that should be captured. This approach avoids redundant checks and efficiently isolates the regions that must remain unchanged.
Interview Quick-Check
Core Logic
DFS explores all 'O's connected to the border, marking them to prevent flipping, effectively separating safe regions from capturable ones.
State & Boundaries
The DFS base case checks for out-of-bounds indices and cells that are not 'O', ensuring recursion terminates correctly.
Common Pitfalls & Bugs
Failing to mark visited 'O's leads to infinite recursion or repeated work; using a temporary marker like 'T' prevents this.
Complexity
Each cell is visited at most once during DFS, ensuring O(m*n) time complexity.
Flip Unmarked 'O's to 'X' to Capture Surrounded Regions
To convert all 'O's not connected to the border into 'X's, effectively capturing surrounded regions.
Restore Marked 'T's Back to 'O's to Finalize Safe Regions
To revert the temporary marker 'T' back to 'O' to restore the original safe regions.
2 more steps with full analysis available on Pro.
Line Analysis
This solution has 4 Critical lines interviewers watch for.
board[r][c] = "T"
Mark the current 'O' cell as 'T' to indicate it is safe.
This marking is the critical step that prevents infinite recursion and correctly identifies safe regions, enabling the algorithm to separate safe from capturable 'O's.
if r < 0 or c < 0 or r == ROWS or c == COLS or board[r][c] != "O":
Check if the current cell is out of bounds or not an 'O'.
This base case prevents invalid access and stops recursion on cells that are either 'X' or already marked, ensuring correctness and termination.
capture(r, c)
Invoke DFS to mark all connected safe 'O's starting from this border cell.
This call triggers the marking of all safe 'O's connected to the border, ensuring they are preserved.
Full line-by-line criticality + rationale for all 21 lines available on Pro.
Test Your Understanding
Why does starting DFS from the border 'O's guarantee that all safe 'O's are marked correctly?
See the answer with Pro.
Related Problems
DFS pattern
Don't just read it. Drill it.
Reconstruct Surrounded Regions from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Surrounded Regions drill