medium tree, graph, dp questions
This commit is contained in:
135
backend/data/questions/binary-tree-level-order.yaml
Normal file
135
backend/data/questions/binary-tree-level-order.yaml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
title: Binary Tree Level Order Traversal
|
||||||
|
slug: binary-tree-level-order
|
||||||
|
difficulty: medium
|
||||||
|
leetcode_id: 102
|
||||||
|
leetcode_url: https://leetcode.com/problems/binary-tree-level-order-traversal/
|
||||||
|
categories:
|
||||||
|
- trees
|
||||||
|
- queue
|
||||||
|
patterns:
|
||||||
|
- bfs
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Given the `root` of a binary tree, return the level order traversal of its nodes' values
|
||||||
|
(i.e., from left to right, level by level).
|
||||||
|
|
||||||
|
constraints: |
|
||||||
|
- The number of nodes in the tree is in the range [0, 2000].
|
||||||
|
- -1000 <= Node.val <= 1000
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- input: "root = [3,9,20,null,null,15,7]"
|
||||||
|
output: "[[3],[9,20],[15,7]]"
|
||||||
|
explanation: "Level 0 has 3, level 1 has 9 and 20, level 2 has 15 and 7."
|
||||||
|
- input: "root = [1]"
|
||||||
|
output: "[[1]]"
|
||||||
|
explanation: "Single node at level 0."
|
||||||
|
- input: "root = []"
|
||||||
|
output: "[]"
|
||||||
|
explanation: "Empty tree returns empty list."
|
||||||
|
|
||||||
|
explanation:
|
||||||
|
approach: |
|
||||||
|
1. Use a queue for BFS traversal
|
||||||
|
2. Track the number of nodes at current level
|
||||||
|
3. Process all nodes at current level before moving to next
|
||||||
|
4. Add children to queue as we process each node
|
||||||
|
5. Collect values for each level in a separate list
|
||||||
|
|
||||||
|
intuition: |
|
||||||
|
BFS naturally visits nodes level by level. By tracking how many nodes are in the queue
|
||||||
|
at the start of each level, we know exactly when one level ends and the next begins.
|
||||||
|
|
||||||
|
The key insight is that after processing all nodes of level k, the queue contains
|
||||||
|
exactly all nodes of level k+1.
|
||||||
|
|
||||||
|
common_pitfalls:
|
||||||
|
- title: Not tracking level boundaries
|
||||||
|
description: |
|
||||||
|
Without tracking level size, you can't separate nodes into their levels.
|
||||||
|
Capture queue size at the start of each level iteration.
|
||||||
|
wrong_approach: "Processing queue without counting level size"
|
||||||
|
correct_approach: "level_size = len(queue); process level_size nodes"
|
||||||
|
|
||||||
|
- title: Forgetting null check
|
||||||
|
description: |
|
||||||
|
Empty tree (null root) should return empty list, not cause an error.
|
||||||
|
|
||||||
|
- title: Using list as queue
|
||||||
|
description: |
|
||||||
|
Using list.pop(0) is O(n). Use collections.deque for O(1) popleft.
|
||||||
|
|
||||||
|
key_takeaways:
|
||||||
|
- BFS with queue is the standard for level-order traversal
|
||||||
|
- Track level size to group nodes by level
|
||||||
|
- This pattern extends to many tree problems (zigzag, right side view, etc.)
|
||||||
|
- deque is more efficient than list for queue operations
|
||||||
|
|
||||||
|
time_complexity: "O(n)"
|
||||||
|
space_complexity: "O(n)"
|
||||||
|
complexity_explanation: |
|
||||||
|
Time: Visit each node exactly once.
|
||||||
|
Space: Queue holds at most one level of nodes, which is O(n) in worst case (complete tree).
|
||||||
|
|
||||||
|
solutions:
|
||||||
|
- approach_name: BFS with Queue (Optimal)
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
class TreeNode:
|
||||||
|
def __init__(self, val=0, left=None, right=None):
|
||||||
|
self.val = val
|
||||||
|
self.left = left
|
||||||
|
self.right = right
|
||||||
|
|
||||||
|
def level_order(root: TreeNode | None) -> list[list[int]]:
|
||||||
|
if not root:
|
||||||
|
return []
|
||||||
|
|
||||||
|
result = []
|
||||||
|
queue = deque([root])
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
level_size = len(queue)
|
||||||
|
level_values = []
|
||||||
|
|
||||||
|
for _ in range(level_size):
|
||||||
|
node = queue.popleft()
|
||||||
|
level_values.append(node.val)
|
||||||
|
|
||||||
|
if node.left:
|
||||||
|
queue.append(node.left)
|
||||||
|
if node.right:
|
||||||
|
queue.append(node.right)
|
||||||
|
|
||||||
|
result.append(level_values)
|
||||||
|
|
||||||
|
return result
|
||||||
|
explanation: |
|
||||||
|
Process nodes level by level using a queue.
|
||||||
|
Track level size to know when to start a new level list.
|
||||||
|
|
||||||
|
- approach_name: DFS with Level Tracking
|
||||||
|
is_optimal: false
|
||||||
|
code: |
|
||||||
|
def level_order(root: TreeNode | None) -> list[list[int]]:
|
||||||
|
result = []
|
||||||
|
|
||||||
|
def dfs(node: TreeNode | None, level: int) -> None:
|
||||||
|
if not node:
|
||||||
|
return
|
||||||
|
|
||||||
|
if level == len(result):
|
||||||
|
result.append([])
|
||||||
|
|
||||||
|
result[level].append(node.val)
|
||||||
|
|
||||||
|
dfs(node.left, level + 1)
|
||||||
|
dfs(node.right, level + 1)
|
||||||
|
|
||||||
|
dfs(root, 0)
|
||||||
|
return result
|
||||||
|
explanation: |
|
||||||
|
DFS alternative: pass level as parameter and append to appropriate list.
|
||||||
|
Same time complexity but uses recursion stack space.
|
||||||
125
backend/data/questions/coin-change.yaml
Normal file
125
backend/data/questions/coin-change.yaml
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
title: Coin Change
|
||||||
|
slug: coin-change
|
||||||
|
difficulty: medium
|
||||||
|
leetcode_id: 322
|
||||||
|
leetcode_url: https://leetcode.com/problems/coin-change/
|
||||||
|
categories:
|
||||||
|
- dynamic-programming
|
||||||
|
- arrays
|
||||||
|
patterns:
|
||||||
|
- dynamic-programming
|
||||||
|
|
||||||
|
description: |
|
||||||
|
You are given an integer array `coins` representing coins of different denominations and an
|
||||||
|
integer `amount` representing a total amount of money.
|
||||||
|
|
||||||
|
Return the fewest number of coins that you need to make up that amount. If that amount of
|
||||||
|
money cannot be made up by any combination of the coins, return -1.
|
||||||
|
|
||||||
|
You may assume that you have an infinite number of each kind of coin.
|
||||||
|
|
||||||
|
constraints: |
|
||||||
|
- 1 <= coins.length <= 12
|
||||||
|
- 1 <= coins[i] <= 2^31 - 1
|
||||||
|
- 0 <= amount <= 10^4
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- input: "coins = [1,2,5], amount = 11"
|
||||||
|
output: "3"
|
||||||
|
explanation: "11 = 5 + 5 + 1"
|
||||||
|
- input: "coins = [2], amount = 3"
|
||||||
|
output: "-1"
|
||||||
|
explanation: "Cannot make amount 3 with only coin 2."
|
||||||
|
- input: "coins = [1], amount = 0"
|
||||||
|
output: "0"
|
||||||
|
explanation: "Amount 0 needs 0 coins."
|
||||||
|
|
||||||
|
explanation:
|
||||||
|
approach: |
|
||||||
|
1. Create a DP array where dp[i] = min coins needed for amount i
|
||||||
|
2. Initialize dp[0] = 0 (zero coins for zero amount)
|
||||||
|
3. For each amount from 1 to target, try each coin
|
||||||
|
4. If coin <= current amount, dp[i] = min(dp[i], dp[i - coin] + 1)
|
||||||
|
5. Return dp[amount] if valid, else -1
|
||||||
|
|
||||||
|
intuition: |
|
||||||
|
This is the classic unbounded knapsack problem. For each amount, we ask: "What's the
|
||||||
|
minimum coins needed if I use coin c as the last coin?"
|
||||||
|
|
||||||
|
If we use coin c last, we need 1 + dp[amount - c] coins. We try all possible "last coins"
|
||||||
|
and take the minimum. This optimal substructure makes it perfect for DP.
|
||||||
|
|
||||||
|
common_pitfalls:
|
||||||
|
- title: Wrong initialization
|
||||||
|
description: |
|
||||||
|
Initialize dp array to infinity (or amount + 1), not 0.
|
||||||
|
dp[0] = 0 is the only base case.
|
||||||
|
wrong_approach: "Initializing all dp values to 0"
|
||||||
|
correct_approach: "dp = [float('inf')] * (amount + 1); dp[0] = 0"
|
||||||
|
|
||||||
|
- title: Not checking if subproblem is solvable
|
||||||
|
description: |
|
||||||
|
Before using dp[i - coin], ensure i >= coin and dp[i - coin] is valid.
|
||||||
|
|
||||||
|
- title: Returning wrong value for impossible case
|
||||||
|
description: |
|
||||||
|
If dp[amount] is still infinity, return -1, not infinity.
|
||||||
|
|
||||||
|
key_takeaways:
|
||||||
|
- Classic unbounded knapsack problem
|
||||||
|
- Bottom-up DP builds solution from smaller amounts
|
||||||
|
- Try each coin as the "last coin" for each amount
|
||||||
|
- Greedy doesn't work here (counterexample: coins=[1,3,4], amount=6)
|
||||||
|
|
||||||
|
time_complexity: "O(amount × coins)"
|
||||||
|
space_complexity: "O(amount)"
|
||||||
|
complexity_explanation: |
|
||||||
|
Time: For each amount (1 to target), we try each coin.
|
||||||
|
Space: DP array of size amount + 1.
|
||||||
|
|
||||||
|
solutions:
|
||||||
|
- approach_name: Bottom-Up DP (Optimal)
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
def coin_change(coins: list[int], amount: int) -> int:
|
||||||
|
dp = [float('inf')] * (amount + 1)
|
||||||
|
dp[0] = 0
|
||||||
|
|
||||||
|
for i in range(1, amount + 1):
|
||||||
|
for coin in coins:
|
||||||
|
if coin <= i and dp[i - coin] != float('inf'):
|
||||||
|
dp[i] = min(dp[i], dp[i - coin] + 1)
|
||||||
|
|
||||||
|
return dp[amount] if dp[amount] != float('inf') else -1
|
||||||
|
explanation: |
|
||||||
|
Build up from amount 0. For each amount, try using each coin as the last coin.
|
||||||
|
Take the minimum of all valid options.
|
||||||
|
|
||||||
|
- approach_name: BFS (Alternative)
|
||||||
|
is_optimal: false
|
||||||
|
code: |
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
def coin_change(coins: list[int], amount: int) -> int:
|
||||||
|
if amount == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
visited = {0}
|
||||||
|
queue = deque([(0, 0)]) # (current_sum, num_coins)
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
current, num_coins = queue.popleft()
|
||||||
|
|
||||||
|
for coin in coins:
|
||||||
|
next_sum = current + coin
|
||||||
|
if next_sum == amount:
|
||||||
|
return num_coins + 1
|
||||||
|
if next_sum < amount and next_sum not in visited:
|
||||||
|
visited.add(next_sum)
|
||||||
|
queue.append((next_sum, num_coins + 1))
|
||||||
|
|
||||||
|
return -1
|
||||||
|
explanation: |
|
||||||
|
BFS finds shortest path in unweighted graph.
|
||||||
|
First time we reach 'amount' is the minimum coins.
|
||||||
|
Less space-efficient than DP for this problem.
|
||||||
157
backend/data/questions/number-of-islands.yaml
Normal file
157
backend/data/questions/number-of-islands.yaml
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
title: Number of Islands
|
||||||
|
slug: number-of-islands
|
||||||
|
difficulty: medium
|
||||||
|
leetcode_id: 200
|
||||||
|
leetcode_url: https://leetcode.com/problems/number-of-islands/
|
||||||
|
categories:
|
||||||
|
- graphs
|
||||||
|
- arrays
|
||||||
|
patterns:
|
||||||
|
- dfs
|
||||||
|
- bfs
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Given an m x n 2D binary grid `grid` which represents a map of '1's (land) and '0's (water),
|
||||||
|
return the number of islands.
|
||||||
|
|
||||||
|
An island is surrounded by water and is formed by connecting adjacent lands horizontally
|
||||||
|
or vertically. You may assume all four edges of the grid are surrounded by water.
|
||||||
|
|
||||||
|
constraints: |
|
||||||
|
- m == grid.length
|
||||||
|
- n == grid[i].length
|
||||||
|
- 1 <= m, n <= 300
|
||||||
|
- grid[i][j] is '0' or '1'
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- input: |
|
||||||
|
grid = [
|
||||||
|
["1","1","1","1","0"],
|
||||||
|
["1","1","0","1","0"],
|
||||||
|
["1","1","0","0","0"],
|
||||||
|
["0","0","0","0","0"]
|
||||||
|
]
|
||||||
|
output: "1"
|
||||||
|
explanation: "All land cells are connected, forming one island."
|
||||||
|
- input: |
|
||||||
|
grid = [
|
||||||
|
["1","1","0","0","0"],
|
||||||
|
["1","1","0","0","0"],
|
||||||
|
["0","0","1","0","0"],
|
||||||
|
["0","0","0","1","1"]
|
||||||
|
]
|
||||||
|
output: "3"
|
||||||
|
explanation: "Three separate connected components of land."
|
||||||
|
|
||||||
|
explanation:
|
||||||
|
approach: |
|
||||||
|
1. Iterate through every cell in the grid
|
||||||
|
2. When a '1' (land) is found, increment island count
|
||||||
|
3. Use DFS/BFS to mark all connected land cells as visited
|
||||||
|
4. Continue iteration until all cells are processed
|
||||||
|
|
||||||
|
intuition: |
|
||||||
|
Each island is a connected component of '1's. We need to count these components.
|
||||||
|
|
||||||
|
When we find an unvisited '1', we've discovered a new island. We then "sink" the entire
|
||||||
|
island by marking all connected '1's as visited (either change to '0' or use a visited set).
|
||||||
|
This ensures we don't count the same island multiple times.
|
||||||
|
|
||||||
|
common_pitfalls:
|
||||||
|
- title: Not marking visited cells
|
||||||
|
description: |
|
||||||
|
Without marking cells as visited, you'll count the same island multiple times
|
||||||
|
or get infinite loops in DFS/BFS.
|
||||||
|
wrong_approach: "Not modifying grid or using visited set"
|
||||||
|
correct_approach: "Mark cell as '0' or add to visited set when processing"
|
||||||
|
|
||||||
|
- title: Diagonal connections
|
||||||
|
description: |
|
||||||
|
Islands only connect horizontally and vertically, not diagonally.
|
||||||
|
Only explore 4 directions, not 8.
|
||||||
|
|
||||||
|
- title: Boundary checks
|
||||||
|
description: |
|
||||||
|
Always check if row/col are within bounds before accessing grid.
|
||||||
|
|
||||||
|
key_takeaways:
|
||||||
|
- Grid problems often reduce to graph traversal
|
||||||
|
- DFS or BFS both work for exploring connected components
|
||||||
|
- Modifying input can serve as "visited" tracking
|
||||||
|
- This pattern applies to many "count components" problems
|
||||||
|
|
||||||
|
time_complexity: "O(m × n)"
|
||||||
|
space_complexity: "O(m × n)"
|
||||||
|
complexity_explanation: |
|
||||||
|
Time: Each cell is visited at most once.
|
||||||
|
Space: DFS recursion stack or BFS queue can hold O(m × n) cells in worst case.
|
||||||
|
|
||||||
|
solutions:
|
||||||
|
- approach_name: DFS (Optimal)
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
def num_islands(grid: list[list[str]]) -> int:
|
||||||
|
if not grid:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
rows, cols = len(grid), len(grid[0])
|
||||||
|
islands = 0
|
||||||
|
|
||||||
|
def dfs(r: int, c: int) -> None:
|
||||||
|
if r < 0 or r >= rows or c < 0 or c >= cols:
|
||||||
|
return
|
||||||
|
if grid[r][c] != '1':
|
||||||
|
return
|
||||||
|
|
||||||
|
grid[r][c] = '0' # Mark as visited
|
||||||
|
|
||||||
|
dfs(r + 1, c)
|
||||||
|
dfs(r - 1, c)
|
||||||
|
dfs(r, c + 1)
|
||||||
|
dfs(r, c - 1)
|
||||||
|
|
||||||
|
for r in range(rows):
|
||||||
|
for c in range(cols):
|
||||||
|
if grid[r][c] == '1':
|
||||||
|
islands += 1
|
||||||
|
dfs(r, c)
|
||||||
|
|
||||||
|
return islands
|
||||||
|
explanation: |
|
||||||
|
When land is found, increment count and sink the entire island using DFS.
|
||||||
|
Modifying the grid serves as our visited marker.
|
||||||
|
|
||||||
|
- approach_name: BFS
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
def num_islands(grid: list[list[str]]) -> int:
|
||||||
|
if not grid:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
rows, cols = len(grid), len(grid[0])
|
||||||
|
islands = 0
|
||||||
|
|
||||||
|
def bfs(start_r: int, start_c: int) -> None:
|
||||||
|
queue = deque([(start_r, start_c)])
|
||||||
|
grid[start_r][start_c] = '0'
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
r, c = queue.popleft()
|
||||||
|
for dr, dc in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
|
||||||
|
nr, nc = r + dr, c + dc
|
||||||
|
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == '1':
|
||||||
|
grid[nr][nc] = '0'
|
||||||
|
queue.append((nr, nc))
|
||||||
|
|
||||||
|
for r in range(rows):
|
||||||
|
for c in range(cols):
|
||||||
|
if grid[r][c] == '1':
|
||||||
|
islands += 1
|
||||||
|
bfs(r, c)
|
||||||
|
|
||||||
|
return islands
|
||||||
|
explanation: |
|
||||||
|
Same logic using BFS instead of DFS.
|
||||||
|
Avoids recursion stack but uses queue space.
|
||||||
124
backend/data/questions/word-search.yaml
Normal file
124
backend/data/questions/word-search.yaml
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
title: Word Search
|
||||||
|
slug: word-search
|
||||||
|
difficulty: medium
|
||||||
|
leetcode_id: 79
|
||||||
|
leetcode_url: https://leetcode.com/problems/word-search/
|
||||||
|
categories:
|
||||||
|
- arrays
|
||||||
|
- recursion
|
||||||
|
patterns:
|
||||||
|
- backtracking
|
||||||
|
- dfs
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Given an m x n grid of characters `board` and a string `word`, return true if `word` exists
|
||||||
|
in the grid.
|
||||||
|
|
||||||
|
The word can be constructed from letters of sequentially adjacent cells, where adjacent cells
|
||||||
|
are horizontally or vertically neighboring. The same letter cell may not be used more than once.
|
||||||
|
|
||||||
|
constraints: |
|
||||||
|
- m == board.length
|
||||||
|
- n == board[i].length
|
||||||
|
- 1 <= m, n <= 6
|
||||||
|
- 1 <= word.length <= 15
|
||||||
|
- board and word consist of only lowercase and uppercase English letters
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- input: 'board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"'
|
||||||
|
output: "true"
|
||||||
|
explanation: "Path exists starting from top-left corner."
|
||||||
|
- input: 'board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"'
|
||||||
|
output: "true"
|
||||||
|
explanation: "Path exists."
|
||||||
|
- input: 'board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"'
|
||||||
|
output: "false"
|
||||||
|
explanation: "Would need to reuse 'B' cell."
|
||||||
|
|
||||||
|
explanation:
|
||||||
|
approach: |
|
||||||
|
1. For each cell, try to start the word from there
|
||||||
|
2. Use DFS with backtracking to explore all paths
|
||||||
|
3. Mark cells as visited during exploration
|
||||||
|
4. Unmark cells when backtracking (restore state)
|
||||||
|
5. If entire word is matched, return true
|
||||||
|
|
||||||
|
intuition: |
|
||||||
|
This is a classic backtracking problem. We explore paths character by character,
|
||||||
|
and if we reach a dead end (no valid next character), we backtrack and try a
|
||||||
|
different direction.
|
||||||
|
|
||||||
|
The key is marking cells as visited during exploration to avoid reusing them,
|
||||||
|
then unmarking when we backtrack to allow other paths to use them.
|
||||||
|
|
||||||
|
common_pitfalls:
|
||||||
|
- title: Not restoring visited state
|
||||||
|
description: |
|
||||||
|
After exploring a path, you must unmark the cell as visited.
|
||||||
|
Otherwise, other paths from earlier cells can't use it.
|
||||||
|
wrong_approach: "Only marking, never unmarking"
|
||||||
|
correct_approach: "Mark before recursion, unmark after"
|
||||||
|
|
||||||
|
- title: Modifying board permanently
|
||||||
|
description: |
|
||||||
|
If you change board[r][c] to mark as visited, restore it after backtracking.
|
||||||
|
|
||||||
|
- title: Checking word completion too late
|
||||||
|
description: |
|
||||||
|
Check if entire word is matched (index == len(word)) at the start of DFS,
|
||||||
|
before any bounds/character checks.
|
||||||
|
|
||||||
|
key_takeaways:
|
||||||
|
- Backtracking = DFS with state restoration
|
||||||
|
- Mark and unmark visited cells around recursive calls
|
||||||
|
- Early termination when full word is found
|
||||||
|
- Grid constraints allow brute force (small board size)
|
||||||
|
|
||||||
|
time_complexity: "O(m × n × 3^L)"
|
||||||
|
space_complexity: "O(L)"
|
||||||
|
complexity_explanation: |
|
||||||
|
Time: Start from each cell, explore up to 3 directions (not the one we came from) for L characters.
|
||||||
|
Space: Recursion depth is at most word length L.
|
||||||
|
|
||||||
|
solutions:
|
||||||
|
- approach_name: DFS with Backtracking (Optimal)
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
def exist(board: list[list[str]], word: str) -> bool:
|
||||||
|
rows, cols = len(board), len(board[0])
|
||||||
|
|
||||||
|
def dfs(r: int, c: int, i: int) -> bool:
|
||||||
|
if i == len(word):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if r < 0 or r >= rows or c < 0 or c >= cols:
|
||||||
|
return False
|
||||||
|
if board[r][c] != word[i]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Mark as visited
|
||||||
|
temp = board[r][c]
|
||||||
|
board[r][c] = '#'
|
||||||
|
|
||||||
|
# Explore all 4 directions
|
||||||
|
found = (
|
||||||
|
dfs(r + 1, c, i + 1) or
|
||||||
|
dfs(r - 1, c, i + 1) or
|
||||||
|
dfs(r, c + 1, i + 1) or
|
||||||
|
dfs(r, c - 1, i + 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Restore (backtrack)
|
||||||
|
board[r][c] = temp
|
||||||
|
|
||||||
|
return found
|
||||||
|
|
||||||
|
for r in range(rows):
|
||||||
|
for c in range(cols):
|
||||||
|
if dfs(r, c, 0):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
explanation: |
|
||||||
|
Try starting from each cell. Use DFS to match characters one by one.
|
||||||
|
Mark cells temporarily, then restore when backtracking.
|
||||||
Reference in New Issue
Block a user