Stage IV: Hard Questions
Greedy Algorithm
Pattern: Advanced Greedy Logic
These problems require a more complex or non-obvious greedy choice, often involving careful consideration of future states or multiple passes.
6. Jump Game II
Problem: You are given a 0-indexed array of integers nums of length n. You are initially positioned at nums[0]. Each element nums[i] represents the maximum length of a forward jump from index i. Return the minimum number of jumps to reach nums[n-1].
Concept Tested: A more advanced greedy choice. Instead of jumping one step at a time, the greedy strategy is to make the jump that reaches the farthest possible index. You maintain a window of the current reachable range and find the best next jump within that range.
Companies: Amazon, Microsoft, Google, Apple
View Python Solution
def jump(nums: list[int]) -> int: jumps = 0 current_farthest = 0 next_farthest = 0
for i in range(len(nums) - 1): next_farthest = max(next_farthest, i + nums[i]) if i == current_farthest: jumps += 1 current_farthest = next_farthest return jumps
7. Task Scheduler
Problem: Given a characters array tasks and a non-negative integer n (the cooldown period), each task takes one unit of time. You need to schedule the tasks such that the same tasks are at least n units of time apart. Return the least number of units of times that the CPU will take to finish all the given tasks.
Concept Tested: A frequency-based greedy approach. The optimal strategy is to always try to schedule the most frequent task. The idle time is determined by the frequency of the most common task and the cooldown period.
Companies: Facebook (Meta), Google, Amazon, Microsoft
View Python Solution
from collections import Counter def leastInterval(tasks: list[str], n: int) -> int: if n == 0: return len(tasks)
counts = Counter(tasks) max_freq = max(counts.values()) # Count how many tasks have the max frequency num_max_freq_tasks = sum(1 for count in counts.values() if count == max_freq) # Calculate the number of blocks needed based on the most frequent task # (max_freq - 1) full blocks, each of size (n + 1) # plus the number of tasks in the last block interval_time = (max_freq - 1) * (n + 1) + num_max_freq_tasks # The result is the max of this calculated time and the actual number of tasks # (in case there's no idle time needed) return max(len(tasks), interval_time)
8. Candy
Problem: There are n children standing in a line. You are giving candies to these children subjected to the following requirements: Each child must have at least one candy, and children with a higher rating get more candies than their neighbors. Return the minimum number of candies you need to have.
Concept Tested: A two-pass greedy algorithm. A single greedy pass is insufficient. The first pass from left-to-right ensures every child has more candies than their left neighbor if their rating is higher. The second pass from right-to-left does the same for the right neighbor.
Companies: Google, Amazon, Facebook (Meta)
View Python Solution
def candy(ratings: list[int]) -> int: n = len(ratings) candies = [1] * n
# First pass: Left to Right for i in range(1, n): if ratings[i] > ratings[i-1]: candies[i] = candies[i-1] + 1 # Second pass: Right to Left for i in range(n - 2, -1, -1): if ratings[i] > ratings[i+1]: candies[i] = max(candies[i], candies[i+1] + 1) return sum(candies)
Backtracking
Pattern: Constraint Satisfaction on a Grid
These are the classic, definitive backtracking problems that require carefully checking a set of complex rules at each step.
6. N-Queens
Problem: The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle.
Concept Tested: Advanced constraint satisfaction. For each placement, you must check that the new queen is not on the same column, row, or diagonal as any previously placed queens.
Companies: Amazon, Google, Microsoft, Facebook (Meta)
View Python Solution
def solveNQueens(n: int) -> list[list[str]]: cols = set() pos_diag = set() # (r + c) neg_diag = set() # (r - c)
result = [] board = [["."] * n for _ in range(n)] def backtrack(r): if r == n: copy = ["".join(row) for row in board] result.append(copy) return for c in range(n): if c in cols or (r + c) in pos_diag or (r - c) in neg_diag: continue # Prune this path # Choose cols.add(c) pos_diag.add(r + c) neg_diag.add(r - c) board[r][c] = "Q" # Explore backtrack(r + 1) # Un-choose cols.remove(c) pos_diag.remove(r + c) neg_diag.remove(r - c) board[r][c] = "." backtrack(0) return result
7. Sudoku Solver
Problem: Write a program to solve a Sudoku puzzle by filling the empty cells.
Concept Tested: Grid-based constraint satisfaction. The choice is which number (1-9) to place in an empty cell, and the constraints are that the number must not already exist in the current row, column, or 3x3 sub-box.
Companies: Amazon, Microsoft, Facebook (Meta), Apple, Uber
View Python Solution
def solveSudoku(board: list[list[str]]) -> None: def is_valid(r, c, digit): for i in range(9): if board[i][c] == digit: return False # Check column if board[r][i] == digit: return False # Check row if board[3 * (r // 3) + i // 3][3 * (c // 3) + i % 3] == digit: return False # Check 3x3 box return True
def backtrack(r, c): if r == 9: # Reached the end of the board return True # Move to the next row if current column is out of bounds next_r, next_c = (r, c + 1) if c < 8 else (r + 1, 0) if board[r][c] != '.': return backtrack(next_r, next_c) for digit_val in range(1, 10): digit_str = str(digit_val) if is_valid(r, c, digit_str): # Choose board[r][c] = digit_str # Explore if backtrack(next_r, next_c): return True # Un-choose (backtrack) board[r][c] = '.' return False backtrack(0, 0)
Pattern: Backtracking with Other Data Structures
This problem requires combining the backtracking search algorithm with another data structure (a Trie) for massive optimization.
8. Word Search II
Problem: Given an m x n board of characters and a list of words, return all words on the board. Each word must 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 in a word.
Concept Tested: Combining a backtracking DFS on the grid with a Trie (Prefix Tree). The Trie allows for extremely efficient pruning: if the current path on the board doesn't form a valid prefix in the dictionary, you can stop exploring that path immediately.
Companies: Amazon, Google, Microsoft, Facebook (Meta)
View Python Solution
class TrieNode: def __init__(self): self.children = {} self.is_word = False
class Solution: def findWords(self, board: list[list[str]], words: list[str]) -> list[str]: root = TrieNode() for w in words: node = root for c in w: if c not in node.children: node.children[c] = TrieNode() node = node.children[c] node.is_word = True
ROWS, COLS = len(board), len(board[0]) result, visited = set(), set() def backtrack(r, c, node, word): if (r < 0 or c < 0 or r >= ROWS or c >= COLS or board[r][c] not in node.children or (r, c) in visited): return # Choose visited.add((r, c)) node = node.children[board[r][c]] word += board[r][c] if node.is_word: result.add(word) # Explore backtrack(r + 1, c, node, word) backtrack(r - 1, c, node, word) backtrack(r, c + 1, node, word) backtrack(r, c - 1, node, word) # Un-choose visited.remove((r, c)) for r in range(ROWS): for c in range(COLS): backtrack(r, c, root, "") return list(result)
Dynamic Programming
Pattern: DP on Strings / Sequences
These problems involve complex state transitions based on string or sequence properties.
13. Edit Distance
Concept Tested: A harder variation of the LCS pattern. The recurrence involves three choices: insert, delete, or replace.
Companies: Google, Amazon, Microsoft
View Python Solution
def minDistance(word1: str, word2: str) -> int: M, N = len(word1), len(word2) dp = [[0] * (N + 1) for _ in range(M + 1)]
for i in range(M + 1): dp[i][N] = M - i for j in range(N + 1): dp[M][j] = N - j for i in range(M - 1, -1, -1): for j in range(N - 1, -1, -1): if word1[i] == word2[j]: dp[i][j] = dp[i + 1][j + 1] else: dp[i][j] = 1 + min(dp[i][j + 1], dp[i + 1][j], dp[i + 1][j + 1]) return dp[0][0]
14. Regular Expression Matching
Concept Tested: A complex 2D DP where the state dp[i][j] represents if s[:i] matches p[:j]. The logic for the * character, which can match zero or more preceding elements, makes the recurrence challenging.
Companies: Facebook (Meta), Google, Amazon, Microsoft
View Python Solution
def isMatch(s: str, p: str) -> bool: memo = {} def dp(i, j): if (i, j) in memo: return memo[(i, j)] if j == len(p): return i == len(s)
first_match = i < len(s) and (p[j] == s[i] or p[j] == '.') if j + 1 < len(p) and p[j+1] == '*': # Two choices for '*': use it zero times, or use it one time res = dp(i, j + 2) or (first_match and dp(i + 1, j)) else: res = first_match and dp(i + 1, j + 1) memo[(i,j)] = res return res return dp(0,0)
Pattern: DP on Intervals
For these problems, the DP state is often defined by an interval `dp[i][j]`.
15. Burst Balloons
Concept Tested: The state dp[i][j] is the max coins from bursting balloons in the range (i, j). The key insight is to define the recurrence around the last balloon to be burst in an interval, which splits the problem into two independent subproblems.
Companies: Google, Amazon, Microsoft
View Python Solution
def maxCoins(nums: list[int]) -> int: nums = [1] + nums + [1] memo = {} def dp(l, r): if l > r: return 0 if (l, r) in memo: return memo[(l,r)]
res = 0 for i in range(l, r + 1): # Assume nums[i] is the last balloon to be burst in (l,r) coins = nums[l-1] * nums[i] * nums[r+1] coins += dp(l, i - 1) + dp(i + 1, r) res = max(res, coins) memo[(l,r)] = res return res return dp(1, len(nums) - 2)
Pattern: DP with Multiple States
These problems require more complex states than just a simple index or two.
16. Best Time to Buy and Sell Stock III
Concept Tested: An extension of simpler stock problems. The DP state must include not just the day, but also the number of transactions completed (k) and whether you are currently holding a stock. dp[i][k][0] = max profit on day i with k transactions and no stock.
Companies: Amazon, Microsoft, Google, Bloomberg
View Python Solution
def maxProfit(prices: list[int]) -> int: # State: # t1_cost: min cost of the first transaction's buy # t1_profit: max profit of the first transaction's sell # t2_cost: min cost of the second transaction's buy # t2_profit: max profit of the second transaction's sell t1_cost, t2_cost = float('inf'), float('inf') t1_profit, t2_profit = 0, 0
for price in prices: # For transaction 1 t1_cost = min(t1_cost, price) t1_profit = max(t1_profit, price - t1_cost) # For transaction 2 t2_cost = min(t2_cost, price - t1_profit) t2_profit = max(t2_profit, price - t2_cost) return t2_profit