Problem
Given an mรn grid of 'X' and 'O' characters, flip every 'O' cell that is completely enclosed by 'X' cells. An 'O' is considered safe โ never flipped โ if it sits on the border or is connected to a border 'O' through a chain of adjacent 'O' cells.
- Input:
board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]] - Output:
[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]] - Explanation: The three interior O's form a cluster with no path to the border, so they are captured; the O in the bottom row sits on the border and is left alone.
Counter-example โ a single-cell board is trivially on the border and nothing is ever captured:
- Input:
board = [["O"]] - Output:
[["O"]]
Intuition
Trying to find surrounded regions by searching inward is tricky โ you'd need to check whether every O cluster can reach the border. It's far simpler to flip the question: start from the border and flood outward, marking every O reachable from an edge as safe. Once that pass is done, any remaining O has no escape route and must be captured.
Solution โ Border-First DFS
Mark all O cells reachable from a border O as temporarily safe, then flip everything that remains unmarked.
- Scan all four border edges (top row, bottom row, left column, right column).
- For each border 'O' found, run DFS in all four directions, overwriting connected O cells with a sentinel 'S' to flag them as safe.
- DFS terminates at grid boundaries, 'X' cells, and already-visited 'S' cells.
- After the full border scan, make one pass over the board: flip 'O' โ 'X' (surrounded) and restore 'S' โ 'O' (safe).
1def solve(board: list[list[str]]) -> None:
2 if not board or not board[0]:
3 return
4 rows, cols = len(board), len(board[0])
5
6 def dfs(row: int, col: int) -> None:
7 if row < 0 or row >= rows or col < 0 or col >= cols:
8 return
9 if board[row][col] != 'O':
10 return
11 board[row][col] = 'S' # sentinel: reachable from border, never captured
12 dfs(row + 1, col)
13 dfs(row - 1, col)
14 dfs(row, col + 1)
15 dfs(row, col - 1)
16
17 for row in range(rows):
18 dfs(row, 0) # left column
19 dfs(row, cols - 1) # right column
20 for col in range(cols):
21 dfs(0, col) # top row
22 dfs(rows - 1, col) # bottom row
23
24 for row in range(rows):
25 for col in range(cols):
26 if board[row][col] == 'O':
27 board[row][col] = 'X' # surrounded โ capture
28 elif board[row][col] == 'S':
29 board[row][col] = 'O' # restore safe cellTime: O(mยทn) โ each cell is visited at most once across all DFS calls.
Space: O(mยทn) โ the DFS call stack can reach the full board size when the entire grid is 'O'.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Border-First DFS | O(mยทn) | O(mยทn) | Always โ standard approach; clean two-pass logic |
Common Mistakes
- Searching inward instead of outward โ starting DFS from every interior O to check whether it reaches the border requires tracking the full frontier per region, which is more complex; seeding from the border is both simpler and faster.
- Flipping O's to X's during the DFS pass โ overwriting in place loses the distinction between original X's and newly captured O's; the sentinel 'S' keeps the two phases separate.
- Treating border cells as candidates for capture โ O's on row 0, row m-1, col 0, or col n-1 are by definition not surrounded; the DFS must start from them, not skip them.
- Scanning fewer than all four borders โ you need to seed DFS from every cell in each of the four edge rows/columns; missing even one edge leaves some safe O's incorrectly captured.
- Recursive DFS stack overflow on large boards โ a 200ร200 board filled with O's produces ~40,000 recursive frames; switch to iterative BFS with a deque for production code on boards near the constraint limit.
Related Problems
Number of Islandsโ same flood fill structure; counts disconnected O components instead of capturing themMax Area of Islandโ flood fill that accumulates region size rather than marking for captureFlood Fillโ textbook single-source flood fill, the direct building block of this problemRotting Orangesโ multi-source BFS spreading outward from known cells, the same border-seeding intuition applied to a timed spread01 Matrixโ multi-source BFS from all 0-cells mirrors seeding from borders; both avoid interior-out searching