Implement 2D word search variants and analyze complexity
Company: Uber
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates proficiency in grid-based string search, traversal techniques (fixed-direction scanning versus flexible-path exploration), algorithm design including backtracking/DFS, and the ability to analyze time and space complexity while accounting for redundancy and edge cases.
Fixed-Direction Word Search (8 Directions)
Constraints
- 1 <= m, n <= 200 (board may also be empty in edge cases)
- 0 <= len(word)
- board[i][j] and word are uppercase English letters
Examples
Input: ([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'ABC')
Expected Output: True
Explanation: A(0,0) -> B(0,1) -> C(0,2) is a straight Eastward ray.
Input: ([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'SEE')
Expected Output: False
Explanation: No single 8-direction straight ray spells SEE; turning is not allowed.
Input: ([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'AFE')
Expected Output: True
Explanation: A(0,0) -> F(1,1) -> E(2,2) along the SE diagonal.
Input: ([['X','Y'],['Y','X']], 'XX')
Expected Output: True
Explanation: X(0,0) -> X(1,1) along the SE diagonal.
Input: ([['A']], 'A')
Expected Output: True
Explanation: Single cell matches a single-character word.
Input: ([['A']], '')
Expected Output: True
Explanation: Empty word is vacuously present.
Input: ([['A','B'],['C','D']], 'ABCD')
Expected Output: False
Explanation: No straight ray of length 4 fits in a 2x2 grid, and the letters do not align.
Input: ([], 'A')
Expected Output: False
Explanation: Empty board cannot contain a non-empty word.
Input: ([['A','B','C'],['D','E','F'],['G','H','I']], 'AEI')
Expected Output: True
Explanation: Main diagonal A(0,0) -> E(1,1) -> I(2,2).
Input: ([['A','B','C'],['D','E','F'],['G','H','I']], 'CEG')
Expected Output: True
Explanation: Anti-diagonal C(0,2) -> E(1,1) -> G(2,0) along SW.
Hints
- There are exactly 8 direction vectors: (-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1). Iterate over them as a fixed list.
- Before walking a ray, compute the end cell start + dir*(L-1); if it is out of bounds, skip that direction entirely (cheap bounds prune).
- Only begin a ray from a cell matching word[0]; break the inner scan on the first mismatch.
Flexible-Path Word Search (4-Neighbor Backtracking)
Constraints
- 1 <= m, n <= 200 (board may also be empty in edge cases)
- 0 <= len(word)
- board[i][j] and word are uppercase English letters
- A cell may not be reused within a single path
Examples
Input: ([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'ABCCED')
Expected Output: True
Explanation: A(0,0)->B(0,1)->C(0,2)->C(1,2)->E(2,2)->D(2,1), turning as needed.
Input: ([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'SEE')
Expected Output: True
Explanation: S(1,3)->E(2,3)->E(2,2).
Input: ([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'ABCB')
Expected Output: False
Explanation: Would require revisiting the B cell, which the no-reuse rule forbids.
Input: ([['A']], 'A')
Expected Output: True
Explanation: Single cell matches a single-character word.
Input: ([['A']], 'B')
Expected Output: False
Explanation: The only cell does not contain B.
Input: ([['A','A']], 'AAA')
Expected Output: False
Explanation: Only two A cells exist; a length-3 path cannot reuse a cell.
Input: ([['A','B'],['C','D']], '')
Expected Output: True
Explanation: Empty word is vacuously present.
Input: ([], 'A')
Expected Output: False
Explanation: Empty board cannot contain a non-empty word.
Input: ([['C','A','A'],['A','A','A'],['B','C','D']], 'AAB')
Expected Output: True
Explanation: A->A->B path exists, e.g. A(0,1)->A(1,1)->... reaching B(2,0) via adjacent A cells.
Input: ([['A','B','C','E'],['S','F','E','S'],['A','D','E','E']], 'ABCESEEEFS')
Expected Output: True
Explanation: A long winding path that turns repeatedly and never reuses a cell.
Hints
- Start a DFS from every cell whose letter equals word[0]; return true as soon as any start succeeds.
- Mark the current cell visited before recursing (e.g. set it to '#') and restore it afterward so other paths can use it (backtracking).
- Prune early: if the current cell does not match word[i], stop immediately. An optional global frequency check (does the board even contain enough of each letter?) speeds up rejections on large grids.