Surrounded Regions
A fourth multi-source BFS from the border inward, but the flip is two-step instead of one: mark every border-connected 'O' as a throwaway 'T' first, then a final sweep turns the untouched 'O's into 'X' and folds the 'T's back to 'O' — the intermediate label is what keeps 'safe' and 'captured' distinguishable until that last pass.
The problem
Implement `solve(board)`, mutating an `m x n` board of `'X'` and `'O'` in place. Any region of `'O'`s that is fully surrounded by `'X'`s — meaning none of its cells touch the border, directly or through a chain of adjacent `'O'`s — gets flipped to `'X'`. Regions that reach the border, or connect to one that does, stay `'O'`.
'Surrounded' is a connectivity property, not a per-cell one: a single interior-looking `'O'` survives if it's chained to a border `'O'` through any path of `'O'`s, so the safe set has to be found by traversal, not by checking each cell's immediate neighbours.
The approach
Same multi-source seed as every earlier grid BFS in this log — scan the four border edges, push every `'O'` found there into the queue, mark each visited — except here 'visited' is encoded directly on the board: draining the queue overwrites each reached cell from `'O'` to `'T'`, a marker that means 'safe, don't touch' rather than a value that appears anywhere in the problem's own alphabet.
That third symbol is the whole trick. Once BFS finishes, the board holds exactly three states — `'X'` (never was an O), `'T'` (an O connected to the border), and `'O'` (an O that never got reached) — and a single final sweep can tell all three apart and act accordingly: leftover `'O'` had no path to the border, so it becomes `'X'`; `'T'` gets folded back to `'O'` since it was always safe.
Without the intermediate marker, the sweep would have nothing to distinguish 'safe O' from 'captured O' — flipping reached cells straight to `'X'` (or leaving them as `'O'`) collapses the two categories the moment BFS overwrites a cell, since both look identical by the time the sweep runs.
The solution
class Solution {
/**
* @param {character[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
solve(board) {
const visited = new Set()
const q = []
const boardRows = board.length
const boardCols = board[0].length
const directions = [[0,1], [0,-1], [1,0], [-1,0]]
// Scanning all the border areas and pushing 'O' into the q for bfs
for (let r = 0; r < boardRows; r++) {
for (let c = 0; c < boardCols; c++) {
const isBorder = r === 0 || r === boardRows - 1 || c === 0 || c === boardCols - 1
if (isBorder && board[r][c] === 'O') {
q.push([r, c])
visited.add(`${r},${c}`)
}
}
}
// BFS from every border 'O', marking each one reachable as 'T'
while (q.length) {
const [r, c] = q.shift()
board[r][c] = 'T'
for (const [dr, dc] of directions) {
const nr = r + dr
const nc = c + dc
const key = `${nr},${nc}`
const nColInbound = nc >= 0 && nc < boardCols
const nRowInbound = nr >= 0 && nr < boardRows
const inBound = nColInbound && nRowInbound
if (
inBound &&
board[nr][nc] === 'O' &&
!visited.has(key)
) {
visited.add(key)
q.push([nr, nc])
}
}
}
// Final sweep: 'O' (never reached border) -> 'X', 'T' (was border-connected) -> 'O'
for (let r = 0; r < boardRows; r++) {
for (let c = 0; c < boardCols; c++) {
if (board[r][c] === 'O') board[r][c] = 'X'
else if (board[r][c] === 'T') board[r][c] = 'O'
}
}
}
}Time O(R * C)Space O(R * C)