Border DFS, then sweep
Time O(rows × cols) Space O(rows × cols)DFS from each border 'O', turning it into '#'. Then flip remaining 'O' to 'X' and '#' back to 'O'.
class Solution {
public void solve(char[][] board) {
int m = board.length, n = board[0].length;
for (int r = 0; r < m; r++) { mark(board, r, 0); mark(board, r, n - 1); }
for (int c = 0; c < n; c++) { mark(board, 0, c); mark(board, m - 1, c); }
for (int r = 0; r < m; r++)
for (int c = 0; c < n; c++)
board[r][c] = board[r][c] == '#' ? 'O' : 'X';
}
private void mark(char[][] b, int r, int c) {
if (r < 0 || c < 0 || r >= b.length || c >= b[0].length || b[r][c] != 'O') return;
b[r][c] = '#';
mark(b, r + 1, c); mark(b, r - 1, c); mark(b, r, c + 1); mark(b, r, c - 1);
}
}Verdict: Two passes over the board.