Title: Algorithms Code — LearnCoachAssist

Q: What is Binary Search and what is its time complexity?
Write the implementation in Python.
A: Binary Search finds a target in a sorted array by repeatedly halving the search space.
Time: O(log n)  |  Space: O(1)
def binary_search(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

Q: What is Bubble Sort and what is its time complexity?
Write the implementation in Python.
A: Bubble Sort repeatedly swaps adjacent elements if they are in the wrong order.
Time: O(n²)  |  Space: O(1)
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        swapped = False
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swapped = True
        if not swapped:
            break
    return arr

Q: What is Merge Sort and what is its time complexity?
Write the implementation in Python.
A: Merge Sort is a divide-and-conquer algorithm that splits the array in half, recursively sorts each half, then merges them.
Time: O(n log n)  |  Space: O(n)
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)
def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i]); i += 1
        else:
            result.append(right[j]); j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

Q: What is Quick Sort and what is its time complexity?
Write the implementation in Python.
A: Quick Sort picks a pivot, partitions elements around it, then recursively sorts each partition.
Time: O(n log n) avg, O(n²) worst  |  Space: O(log n)
def quick_sort(arr, lo=0, hi=None):
    if hi is None:
        hi = len(arr) - 1
    if lo < hi:
        pivot_idx = partition(arr, lo, hi)
        quick_sort(arr, lo, pivot_idx - 1)
        quick_sort(arr, pivot_idx + 1, hi)
def partition(arr, lo, hi):
    pivot = arr[hi]
    i = lo - 1
    for j in range(lo, hi):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i + 1], arr[hi] = arr[hi], arr[i + 1]
    return i + 1

Q: What is BFS (Breadth-First Search) and what is its time complexity?
Write the implementation in Python.
A: BFS explores a graph level by level using a queue. Used for shortest path in unweighted graphs.
Time: O(V + E)  |  Space: O(V)
from collections import deque
def bfs(graph, start):
    visited = set([start])
    queue = deque([start])
    result = []
    while queue:
        node = queue.popleft()
        result.append(node)
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)
    return result

Q: What is DFS (Depth-First Search) and what is its time complexity?
Write the implementation in Python (iterative and recursive).
A: DFS explores a graph by going as deep as possible before backtracking.
Time: O(V + E)  |  Space: O(V)
Iterative:
def dfs_iterative(graph, start):
    visited = set()
    stack = [start]
    result = []
    while stack:
        node = stack.pop()
        if node not in visited:
            visited.add(node)
            result.append(node)
            for neighbor in graph[node]:
                stack.append(neighbor)
    return result
Recursive:
def dfs_recursive(graph, node, visited=None):
    if visited is None:
        visited = set()
    visited.add(node)
    for neighbor in graph[node]:
        if neighbor not in visited:
            dfs_recursive(graph, neighbor, visited)
    return visited

Q: What is Dijkstra's Algorithm and what is its time complexity?
Write the implementation in Python.
A: Dijkstra's finds the shortest path from a source to all vertices in a weighted graph with non-negative weights.
Time: O((V + E) log V) with min-heap  |  Space: O(V)
import heapq
def dijkstra(graph, start):
    dist = {node: float('inf') for node in graph}
    dist[start] = 0
    heap = [(0, start)]
    while heap:
        d, u = heapq.heappop(heap)
        if d > dist[u]:
            continue
        for v, weight in graph[u]:
            if dist[u] + weight < dist[v]:
                dist[v] = dist[u] + weight
                heapq.heappush(heap, (dist[v], v))
    return dist

Q: What is the Two Pointers technique?
Write a Python example: find a pair in a sorted array that sums to a target.
A: Two Pointers uses two indices moving toward each other (or in the same direction) to solve problems on sorted arrays or linked lists in O(n).
Time: O(n)  |  Space: O(1)
def two_sum_sorted(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo < hi:
        s = arr[lo] + arr[hi]
        if s == target:
            return (lo, hi)
        elif s < target:
            lo += 1
        else:
            hi -= 1
    return None

Q: What is the Sliding Window technique?
Write a Python example: find the max sum of a subarray of size k.
A: Sliding Window maintains a window of elements and slides it across the array, adding/removing one element at a time.
Time: O(n)  |  Space: O(1)
def max_sum_subarray(arr, k):
    window_sum = sum(arr[:k])
    max_sum = window_sum
    for i in range(k, len(arr)):
        window_sum += arr[i] - arr[i - k]
        max_sum = max(max_sum, window_sum)
    return max_sum

Q: What is Dynamic Programming (Fibonacci)?
Write top-down (memoization) and bottom-up (tabulation) in Python.
A: DP solves problems by breaking them into overlapping subproblems and storing results to avoid recomputation.
Top-down (Memoization): O(n) time, O(n) space
def fib_memo(n, memo={}):
    if n <= 1:
        return n
    if n not in memo:
        memo[n] = fib_memo(n-1) + fib_memo(n-2)
    return memo[n]
Bottom-up (Tabulation): O(n) time, O(1) space
def fib_tab(n):
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

Q: What is Kadane's Algorithm?
Write the implementation in Python.
A: Kadane's Algorithm finds the maximum sum contiguous subarray in O(n) time.
Time: O(n)  |  Space: O(1)
def max_subarray(arr):
    max_ending_here = arr[0]
    max_so_far = arr[0]
    for num in arr[1:]:
        max_ending_here = max(num, max_ending_here + num)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far

Q: What is the 0/1 Knapsack problem?
Write the DP solution in Python.
A: Given items with weights and values and a capacity W, find the maximum value without exceeding W. Each item is used at most once.
Time: O(n * W)  |  Space: O(n * W)
def knapsack(weights, values, W):
    n = len(weights)
    dp = [[0] * (W + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(W + 1):
            dp[i][w] = dp[i-1][w]
            if weights[i-1] <= w:
                dp[i][w] = max(dp[i][w],
                    dp[i-1][w - weights[i-1]] + values[i-1])
    return dp[n][W]

Q: What is Topological Sort and when is it used?
Write the implementation using BFS (Kahn's Algorithm) in Python.
A: Topological Sort gives a linear ordering of vertices in a DAG such that for every edge u→v, u comes before v. Used for task scheduling, build systems, course prerequisites.
Time: O(V + E)  |  Space: O(V)
from collections import deque
def topological_sort(graph, num_nodes):
    in_degree = [0] * num_nodes
    for u in graph:
        for v in graph[u]:
            in_degree[v] += 1
    queue = deque([i for i in range(num_nodes)
                   if in_degree[i] == 0])
    order = []
    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph.get(node, []):
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)
    if len(order) != num_nodes:
        return []  # cycle detected
    return order

Q: What is Union-Find (Disjoint Set Union) and when is it used?
Write the implementation in Python.
A: Union-Find tracks elements partitioned into disjoint sets. Used for connected components, Kruskal's MST, cycle detection.
Time per operation: ~O(1) amortized (with path compression + union by rank)
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py:
            return False
        if self.rank[px] < self.rank[py]:
            px, py = py, px
        self.parent[py] = px
        if self.rank[px] == self.rank[py]:
            self.rank[px] += 1
        return True

Q: What is Insertion Sort and what is its time complexity?
Write the implementation in Python.
A: Insertion Sort builds the sorted array one element at a time by inserting each element into its correct position. Efficient for small or nearly sorted arrays.
Time: O(n²) worst, O(n) best  |  Space: O(1)
def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key
    return arr

Q: What is Selection Sort and what is its time complexity?
Write the implementation in Python.
A: Selection Sort finds the minimum element in the unsorted part and swaps it into position. Always O(n²) regardless of input.
Time: O(n²)  |  Space: O(1)
def selection_sort(arr):
    n = len(arr)
    for i in range(n):
        min_idx = i
        for j in range(i + 1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        arr[i], arr[min_idx] = arr[min_idx], arr[i]
    return arr

Q: What is Counting Sort and when should you use it?
Write the implementation in Python.
A: Counting Sort counts occurrences of each value and reconstructs the array. Use when the range of values (k) is not significantly larger than n.
Time: O(n + k)  |  Space: O(k)
def counting_sort(arr):
    if not arr:
        return arr
    min_val, max_val = min(arr), max(arr)
    count = [0] * (max_val - min_val + 1)
    for num in arr:
        count[num - min_val] += 1
    result = []
    for i, c in enumerate(count):
        result.extend([i + min_val] * c)
    return result

Q: What is a Binary Search Tree (BST)?
Write insert and search operations in Python.
A: A BST is a binary tree where for each node, left children < node < right children.
Search/Insert: O(log n) avg, O(n) worst  |  Space: O(n)
class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left = self.right = None
def insert(root, val):
    if not root:
        return TreeNode(val)
    if val < root.val:
        root.left = insert(root.left, val)
    else:
        root.right = insert(root.right, val)
    return root
def search(root, val):
    if not root or root.val == val:
        return root
    if val < root.val:
        return search(root.left, val)
    return search(root.right, val)

Q: What are the three tree traversal orders?
Write inorder, preorder, and postorder traversal in Python.
A: Inorder (Left, Root, Right) — gives sorted order for BST
Preorder (Root, Left, Right) — used to copy/serialize a tree
Postorder (Left, Right, Root) — used to delete a tree
Time: O(n)  |  Space: O(h)
def inorder(root):
    if not root: return []
    return inorder(root.left) + [root.val] + inorder(root.right)
def preorder(root):
    if not root: return []
    return [root.val] + preorder(root.left) + preorder(root.right)
def postorder(root):
    if not root: return []
    return postorder(root.left) + postorder(root.right) + [root.val]

Q: What is a Heap / Priority Queue?
Write a Min-Heap implementation in Python.
A: A heap is a complete binary tree where each parent is ≤ (min-heap) or ≥ (max-heap) its children. Used for priority queues, top-k problems, and sorting.
Push/Pop: O(log n)  |  Peek: O(1)
import heapq
# Python heapq is a min-heap
nums = [3, 1, 4, 1, 5]
heapq.heapify(nums)          # O(n)
heapq.heappush(nums, 2)      # O(log n)
smallest = heapq.heappop(nums)  # O(log n)
# For max-heap, negate values
max_heap = []
heapq.heappush(max_heap, -val)
largest = -heapq.heappop(max_heap)
# Top k smallest
top_k = heapq.nsmallest(k, nums)  # O(n log k)

Q: What is the Longest Common Subsequence (LCS) problem?
Write the DP solution in Python.
A: LCS finds the longest subsequence common to two sequences. A subsequence doesn't need to be contiguous.
Time: O(m * n)  |  Space: O(m * n)
def lcs(s1, s2):
    m, n = len(s1), len(s2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[m][n]
# lcs("abcde", "ace") → 3 ("ace")

Q: What is the Bellman-Ford algorithm and how does it differ from Dijkstra's?
Write the implementation in Python.
A: Bellman-Ford finds shortest paths from a source and handles negative weights (unlike Dijkstra). It can also detect negative cycles.
Time: O(V * E)  |  Space: O(V)
def bellman_ford(num_nodes, edges, src):
    dist = [float('inf')] * num_nodes
    dist[src] = 0
    for _ in range(num_nodes - 1):
        for u, v, w in edges:
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
    # Detect negative cycle
    for u, v, w in edges:
        if dist[u] + w < dist[v]:
            return None  # negative cycle exists
    return dist

Q: What is the Floyd-Warshall algorithm?
Write the implementation in Python.
A: Floyd-Warshall finds shortest paths between all pairs of vertices. Works with negative weights (but no negative cycles).
Time: O(V³)  |  Space: O(V²)
def floyd_warshall(dist):
    n = len(dist)
    # dist[i][j] = weight of edge i→j, or inf
    for k in range(n):
        for i in range(n):
            for j in range(n):
                dist[i][j] = min(dist[i][j],
                    dist[i][k] + dist[k][j])
    return dist

Q: What is Backtracking?
Write a Python example: generate all permutations of a list.
A: Backtracking explores all possibilities by building solutions incrementally and abandoning (pruning) paths that cannot lead to a valid solution.
Time: O(n!) for permutations  |  Space: O(n)
def permutations(nums):
    result = []
    def backtrack(path, remaining):
        if not remaining:
            result.append(path[:])
            return
        for i in range(len(remaining)):
            path.append(remaining[i])
            backtrack(path, remaining[:i] + remaining[i+1:])
            path.pop()
    backtrack([], nums)
    return result
# permutations([1,2,3]) → [[1,2,3],[1,3,2],...]

Q: What is the Binary Search pattern for "find the first/last position"?
Write bisect_left and bisect_right in Python.
A: Use modified binary search to find insertion points (lower/upper bounds). Essential for problems like "first occurrence" or "count of elements in range".
Time: O(log n)  |  Space: O(1)
def bisect_left(arr, target):
    lo, hi = 0, len(arr)
    while lo < hi:
        mid = (lo + hi) // 2
        if arr[mid] < target:
            lo = mid + 1
        else:
            hi = mid
    return lo
def bisect_right(arr, target):
    lo, hi = 0, len(arr)
    while lo < hi:
        mid = (lo + hi) // 2
        if arr[mid] <= target:
            lo = mid + 1
        else:
            hi = mid
    return lo
# Count of target in sorted array:
# bisect_right(arr, t) - bisect_left(arr, t)

Q: What is a Trie (Prefix Tree)?
Write the implementation in Python.
A: A Trie stores strings character by character in a tree. Used for autocomplete, spell check, prefix matching.
Insert/Search: O(m) where m = word length  |  Space: O(n * m)
class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False
class Trie:
    def __init__(self):
        self.root = TrieNode()
    def insert(self, word):
        node = self.root
        for ch in word:
            if ch not in node.children:
                node.children[ch] = TrieNode()
            node = node.children[ch]
        node.is_end = True
    def search(self, word):
        node = self._find(word)
        return node is not None and node.is_end
    def starts_with(self, prefix):
        return self._find(prefix) is not None
    def _find(self, word):
        node = self.root
        for ch in word:
            if ch not in node.children:
                return None
            node = node.children[ch]
        return node

Q: What is the fast/slow pointer (Floyd's Cycle Detection)?
Write a Python example: detect a cycle in a linked list.
A: Two pointers move at different speeds. If there's a cycle, the fast pointer will eventually meet the slow pointer.
Time: O(n)  |  Space: O(1)
def has_cycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False
def find_cycle_start(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            slow = head
            while slow != fast:
                slow = slow.next
                fast = fast.next
            return slow
    return None

Q: What is a Monotonic Stack and when is it used?
Write a Python example: next greater element.
A: A Monotonic Stack maintains elements in increasing or decreasing order. Used for next greater/smaller element problems.
Time: O(n)  |  Space: O(n)
def next_greater_element(arr):
    n = len(arr)
    result = [-1] * n
    stack = []  # stores indices
    for i in range(n):
        while stack and arr[stack[-1]] < arr[i]:
            result[stack.pop()] = arr[i]
        stack.append(i)
    return result
# next_greater_element([2, 1, 2, 4, 3])
# → [4, 2, 4, -1, -1]

Q: What is Kruskal's Minimum Spanning Tree algorithm?
Write the implementation in Python.
A: Kruskal's builds an MST by greedily adding the cheapest edge that doesn't form a cycle, using Union-Find.
Time: O(E log E)  |  Space: O(V)
def kruskal(num_nodes, edges):
    # edges = [(weight, u, v), ...]
    edges.sort()
    uf = UnionFind(num_nodes)
    mst = []
    total_weight = 0
    for w, u, v in edges:
        if uf.union(u, v):
            mst.append((u, v, w))
            total_weight += w
            if len(mst) == num_nodes - 1:
                break
    return mst, total_weight

Q: What is the Coin Change problem (DP)?
Write the implementation in Python.
A: Find the minimum number of coins to make a target amount, or -1 if impossible.
Time: O(amount * n)  |  Space: O(amount)
def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i:
                dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1
# coin_change([1, 5, 10, 25], 30) → 2 (25+5)

Q: What is the Longest Increasing Subsequence (LIS) problem?
Write the O(n log n) solution in Python.
A: LIS finds the longest strictly increasing subsequence. The O(n log n) approach uses a patience sorting technique with binary search.
Time: O(n log n)  |  Space: O(n)
from bisect import bisect_left
def lis(nums):
    tails = []
    for num in nums:
        pos = bisect_left(tails, num)
        if pos == len(tails):
            tails.append(num)
        else:
            tails[pos] = num
    return len(tails)
# lis([10, 9, 2, 5, 3, 7, 101, 18]) → 4
# One LIS: [2, 3, 7, 101]

Q: What is Heap Sort and what is its time complexity?
Write the implementation in Python.
A: Heap Sort builds a max-heap, then repeatedly extracts the max to build the sorted array from right to left. In-place, not stable.
Time: O(n log n)  |  Space: O(1)
def heap_sort(arr):
    n = len(arr)
    def heapify(n, i):
        largest = i
        l, r = 2*i + 1, 2*i + 2
        if l < n and arr[l] > arr[largest]:
            largest = l
        if r < n and arr[r] > arr[largest]:
            largest = r
        if largest != i:
            arr[i], arr[largest] = arr[largest], arr[i]
            heapify(n, largest)
    for i in range(n // 2 - 1, -1, -1):
        heapify(n, i)
    for i in range(n - 1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]
        heapify(i, 0)
    return arr

Q: What is Radix Sort and when should you use it?
Write the implementation in Python.
A: Radix Sort sorts integers digit by digit from least significant to most significant using a stable sort (counting sort) at each digit.
Time: O(d * (n + k)) where d = digits, k = base  |  Space: O(n + k)
def radix_sort(arr):
    if not arr:
        return arr
    max_val = max(arr)
    exp = 1
    while max_val // exp > 0:
        counting_sort_by_digit(arr, exp)
        exp *= 10
    return arr
def counting_sort_by_digit(arr, exp):
    n = len(arr)
    output = [0] * n
    count = [0] * 10
    for num in arr:
        idx = (num // exp) % 10
        count[idx] += 1
    for i in range(1, 10):
        count[i] += count[i - 1]
    for i in range(n - 1, -1, -1):
        idx = (arr[i] // exp) % 10
        output[count[idx] - 1] = arr[i]
        count[idx] -= 1
    arr[:] = output

Q: What is Quick Select and what is its time complexity?
Write the implementation in Python.
A: Quick Select finds the k-th smallest element without fully sorting. Uses partitioning like Quick Sort but only recurses into one side.
Time: O(n) avg, O(n²) worst  |  Space: O(1)
def quick_select(arr, k):
    def select(lo, hi):
        pivot = arr[hi]
        i = lo
        for j in range(lo, hi):
            if arr[j] <= pivot:
                arr[i], arr[j] = arr[j], arr[i]
                i += 1
        arr[i], arr[hi] = arr[hi], arr[i]
        if i == k:
            return arr[i]
        elif i < k:
            return select(i + 1, hi)
        else:
            return select(lo, i - 1)
    return select(0, len(arr) - 1)
# quick_select([3,1,4,1,5,9], 2) → 3 (3rd smallest)

Q: What is the Dutch National Flag algorithm (3-way partition)?
Write the implementation in Python.
A: Sorts an array with only 3 distinct values (e.g., 0, 1, 2) in a single pass. Uses three pointers: lo, mid, hi.
Time: O(n)  |  Space: O(1)
def sort_colors(arr):
    lo, mid, hi = 0, 0, len(arr) - 1
    while mid <= hi:
        if arr[mid] == 0:
            arr[lo], arr[mid] = arr[mid], arr[lo]
            lo += 1
            mid += 1
        elif arr[mid] == 1:
            mid += 1
        else:
            arr[mid], arr[hi] = arr[hi], arr[mid]
            hi -= 1
    return arr
# sort_colors([2,0,1,2,0,1]) → [0,0,1,1,2,2]

Q: What is the Edit Distance (Levenshtein Distance) problem?
Write the DP solution in Python.
A: Edit Distance finds the minimum number of insertions, deletions, and substitutions to transform one string into another.
Time: O(m * n)  |  Space: O(m * n)
def edit_distance(s1, s2):
    m, n = len(s1), len(s2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(m + 1):
        dp[i][0] = i
    for j in range(n + 1):
        dp[0][j] = j
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = 1 + min(
                    dp[i-1][j],    # delete
                    dp[i][j-1],    # insert
                    dp[i-1][j-1])  # replace
    return dp[m][n]
# edit_distance("kitten", "sitting") → 3

Q: What is the Prefix Sum technique?
Write a Python example: range sum query.
A: Prefix Sum precomputes cumulative sums so any range sum query can be answered in O(1).
Build: O(n)  |  Query: O(1)  |  Space: O(n)
def build_prefix_sum(arr):
    prefix = [0] * (len(arr) + 1)
    for i in range(len(arr)):
        prefix[i + 1] = prefix[i] + arr[i]
    return prefix
def range_sum(prefix, l, r):
    return prefix[r + 1] - prefix[l]
# arr = [1, 2, 3, 4, 5]
# prefix = [0, 1, 3, 6, 10, 15]
# range_sum(prefix, 1, 3) → 9  (2+3+4)

Q: What is the Merge Intervals algorithm?
Write the implementation in Python.
A: Merge overlapping intervals by sorting by start time, then greedily merging when intervals overlap.
Time: O(n log n)  |  Space: O(n)
def merge_intervals(intervals):
    intervals.sort(key=lambda x: x[0])
    merged = [intervals[0]]
    for start, end in intervals[1:]:
        if start <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], end)
        else:
            merged.append([start, end])
    return merged
# merge_intervals([[1,3],[2,6],[8,10],[15,18]])
# → [[1,6],[8,10],[15,18]]

Q: How do you reverse a singly linked list?
Write iterative and recursive solutions in Python.
A: Reverse pointers one by one. Track prev, curr, next pointers.
Time: O(n)  |  Space: O(1) iterative, O(n) recursive
Iterative:
def reverse_list(head):
    prev = None
    curr = head
    while curr:
        nxt = curr.next
        curr.next = prev
        prev = curr
        curr = nxt
    return prev
Recursive:
def reverse_list_rec(head):
    if not head or not head.next:
        return head
    new_head = reverse_list_rec(head.next)
    head.next.next = head
    head.next = None
    return new_head

Q: What is the N-Queens problem (backtracking)?
Write the implementation in Python.
A: Place N queens on an N×N board so no two queens attack each other. Uses backtracking to try each column per row.
Time: O(N!)  |  Space: O(N)
def solve_n_queens(n):
    results = []
    def backtrack(row, cols, diag1, diag2, board):
        if row == n:
            results.append(["".join(r) for r in board])
            return
        for col in range(n):
            if col in cols or (row-col) in diag1 or (row+col) in diag2:
                continue
            board[row][col] = 'Q'
            backtrack(row+1, cols|{col},
                diag1|{row-col}, diag2|{row+col}, board)
            board[row][col] = '.'
    board = [['.']*n for _ in range(n)]
    backtrack(0, set(), set(), set(), board)
    return results

Q: How do you generate all subsets (power set)?
Write iterative and recursive solutions in Python.
A: The power set of n elements has 2ⁿ subsets.
Time: O(n * 2ⁿ)  |  Space: O(n * 2ⁿ)
Iterative:
def subsets_iter(nums):
    result = [[]]
    for num in nums:
        result += [s + [num] for s in result]
    return result
Bitmask:
def subsets_bitmask(nums):
    n = len(nums)
    result = []
    for mask in range(1 << n):
        subset = [nums[i] for i in range(n) if mask & (1 << i)]
        result.append(subset)
    return result

Q: How do you generate all combinations of k elements?
Write the implementation in Python.
A: Choose k elements from n without regard to order. Uses backtracking with a start index to avoid duplicates.
Time: O(C(n,k) * k)  |  Space: O(k)
def combinations(n, k):
    result = []
    def backtrack(start, path):
        if len(path) == k:
            result.append(path[:])
            return
        for i in range(start, n + 1):
            path.append(i)
            backtrack(i + 1, path)
            path.pop()
    backtrack(1, [])
    return result
# combinations(4, 2) → [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]

Q: What is the Word Break problem (DP)?
Write the implementation in Python.
A: Given a string and a dictionary, determine if the string can be segmented into dictionary words.
Time: O(n² * m)  |  Space: O(n)
def word_break(s, word_dict):
    n = len(s)
    dp = [False] * (n + 1)
    dp[0] = True
    for i in range(1, n + 1):
        for word in word_dict:
            wl = len(word)
            if i >= wl and dp[i - wl] and s[i-wl:i] == word:
                dp[i] = True
                break
    return dp[n]
# word_break("leetcode", {"leet", "code"}) → True

Q: What is the House Robber problem (DP)?
Write the implementation in Python.
A: Rob houses along a street where adjacent houses cannot both be robbed. Maximize total money.
Time: O(n)  |  Space: O(1)
def rob(nums):
    if not nums:
        return 0
    prev2, prev1 = 0, 0
    for num in nums:
        curr = max(prev1, prev2 + num)
        prev2 = prev1
        prev1 = curr
    return prev1
# rob([2, 7, 9, 3, 1]) → 12 (2 + 9 + 1)

Q: What is the Climbing Stairs problem (DP)?
Write the implementation in Python.
A: Count the number of ways to climb n stairs taking 1 or 2 steps at a time. Equivalent to Fibonacci.
Time: O(n)  |  Space: O(1)
def climb_stairs(n):
    if n <= 2:
        return n
    a, b = 1, 2
    for _ in range(3, n + 1):
        a, b = b, a + b
    return b
# climb_stairs(5) → 8
# Ways: 11111, 1112, 1121, 1211, 2111, 122, 212, 221

Q: What is the Matrix BFS pattern (number of islands)?
Write the implementation in Python.
A: Count connected components of '1's in a 2D grid using BFS/DFS. Mark visited cells to avoid revisiting.
Time: O(m * n)  |  Space: O(m * n)
from collections import deque
def num_islands(grid):
    if not grid:
        return 0
    rows, cols = len(grid), len(grid[0])
    count = 0
    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == '1':
                count += 1
                queue = deque([(r, c)])
                grid[r][c] = '0'
                while queue:
                    x, y = queue.popleft()
                    for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]:
                        nx, ny = x+dx, y+dy
                        if 0<=nx                            grid[nx][ny] = '0'
                            queue.append((nx, ny))
    return count

Q: What is the Lowest Common Ancestor (LCA) of a Binary Tree?
Write the implementation in Python.
A: LCA is the deepest node that is an ancestor of both given nodes. For a general binary tree, use recursive DFS.
Time: O(n)  |  Space: O(h)
def lca(root, p, q):
    if not root or root == p or root == q:
        return root
    left = lca(root.left, p, q)
    right = lca(root.right, p, q)
    if left and right:
        return root
    return left or right
For a BST, use the sorted property:
def lca_bst(root, p, q):
    while root:
        if p.val < root.val and q.val < root.val:
            root = root.left
        elif p.val > root.val and q.val > root.val:
            root = root.right
        else:
            return root

Q: What is the KMP (Knuth-Morris-Pratt) string matching algorithm?
Write the implementation in Python.
A: KMP searches for a pattern in a text by precomputing a failure function (LPS array) to skip redundant comparisons.
Time: O(n + m)  |  Space: O(m)
def kmp_search(text, pattern):
    def build_lps(p):
        lps = [0] * len(p)
        length = 0
        i = 1
        while i < len(p):
            if p[i] == p[length]:
                length += 1
                lps[i] = length
                i += 1
            elif length:
                length = lps[length - 1]
            else:
                lps[i] = 0
                i += 1
        return lps
    lps = build_lps(pattern)
    i = j = 0
    matches = []
    while i < len(text):
        if text[i] == pattern[j]:
            i += 1; j += 1
        if j == len(pattern):
            matches.append(i - j)
            j = lps[j - 1]
        elif i < len(text) and text[i] != pattern[j]:
            j = lps[j - 1] if j else 0
            if j == 0 and text[i] != pattern[0]:
                i += 1
    return matches

Q: What is the Rabin-Karp string matching algorithm?
Write the implementation in Python.
A: Rabin-Karp uses a rolling hash to compare pattern and text windows. Efficient for multiple pattern search.
Time: O(n + m) avg, O(n * m) worst  |  Space: O(1)
def rabin_karp(text, pattern):
    n, m = len(text), len(pattern)
    base, mod = 256, 101
    p_hash = t_hash = 0
    h = pow(base, m - 1, mod)
    matches = []
    for i in range(m):
        p_hash = (base * p_hash + ord(pattern[i])) % mod
        t_hash = (base * t_hash + ord(text[i])) % mod
    for i in range(n - m + 1):
        if p_hash == t_hash:
            if text[i:i+m] == pattern:
                matches.append(i)
        if i < n - m:
            t_hash = (base * (t_hash - ord(text[i]) * h)
                      + ord(text[i + m])) % mod
            t_hash = (t_hash + mod) % mod
    return matches

Q: What is Prim's Minimum Spanning Tree algorithm?
Write the implementation in Python.
A: Prim's builds an MST by greedily adding the cheapest edge connecting a visited node to an unvisited node, using a min-heap.
Time: O(E log V)  |  Space: O(V + E)
import heapq
def prim(graph, start=0):
    visited = set()
    mst = []
    total = 0
    heap = [(0, start, -1)]  # (weight, node, parent)
    while heap and len(visited) < len(graph):
        w, u, parent = heapq.heappop(heap)
        if u in visited:
            continue
        visited.add(u)
        if parent != -1:
            mst.append((parent, u, w))
            total += w
        for v, weight in graph[u]:
            if v not in visited:
                heapq.heappush(heap, (weight, v, u))
    return mst, total

Q: What is a Graph Bipartite Check?
Write the implementation in Python using BFS.
A: A graph is bipartite if nodes can be colored with 2 colors such that no two adjacent nodes share a color. Equivalent to checking for odd-length cycles.
Time: O(V + E)  |  Space: O(V)
from collections import deque
def is_bipartite(graph):
    n = len(graph)
    color = [-1] * n
    for start in range(n):
        if color[start] != -1:
            continue
        queue = deque([start])
        color[start] = 0
        while queue:
            node = queue.popleft()
            for neighbor in graph[node]:
                if color[neighbor] == -1:
                    color[neighbor] = 1 - color[node]
                    queue.append(neighbor)
                elif color[neighbor] == color[node]:
                    return False
    return True

Q: What is Tarjan's algorithm for Strongly Connected Components?
Write the implementation in Python.
A: Tarjan's finds all SCCs in a directed graph using DFS with a stack. An SCC is a maximal set of vertices where every vertex is reachable from every other.
Time: O(V + E)  |  Space: O(V)
def tarjan_scc(graph, n):
    idx = [0]
    stack = []
    on_stack = [False] * n
    index = [-1] * n
    lowlink = [-1] * n
    sccs = []
    def strongconnect(v):
        index[v] = lowlink[v] = idx[0]
        idx[0] += 1
        stack.append(v)
        on_stack[v] = True
        for w in graph.get(v, []):
            if index[w] == -1:
                strongconnect(w)
                lowlink[v] = min(lowlink[v], lowlink[w])
            elif on_stack[w]:
                lowlink[v] = min(lowlink[v], index[w])
        if lowlink[v] == index[v]:
            scc = []
            while True:
                w = stack.pop()
                on_stack[w] = False
                scc.append(w)
                if w == v:
                    break
            sccs.append(scc)
    for v in range(n):
        if index[v] == -1:
            strongconnect(v)
    return sccs

Q: What is a Binary Indexed Tree (Fenwick Tree)?
Write the implementation in Python.
A: A Fenwick Tree supports prefix sum queries and point updates in O(log n). More space-efficient than a Segment Tree.
Query/Update: O(log n)  |  Space: O(n)
class BIT:
    def __init__(self, n):
        self.n = n
        self.tree = [0] * (n + 1)
    def update(self, i, delta):
        i += 1  # 1-indexed
        while i <= self.n:
            self.tree[i] += delta
            i += i & (-i)
    def query(self, i):
        i += 1
        s = 0
        while i > 0:
            s += self.tree[i]
            i -= i & (-i)
        return s
    def range_query(self, l, r):
        return self.query(r) - (self.query(l-1) if l > 0 else 0)

Q: What is a Segment Tree?
Write the implementation in Python (range sum query + point update).
A: A Segment Tree supports range queries and point/range updates in O(log n). More flexible than Fenwick Tree.
Build: O(n)  |  Query/Update: O(log n)  |  Space: O(n)
class SegTree:
    def __init__(self, arr):
        self.n = len(arr)
        self.tree = [0] * (2 * self.n)
        for i in range(self.n):
            self.tree[self.n + i] = arr[i]
        for i in range(self.n - 1, 0, -1):
            self.tree[i] = self.tree[2*i] + self.tree[2*i+1]
    def update(self, i, val):
        i += self.n
        self.tree[i] = val
        while i > 1:
            i //= 2
            self.tree[i] = self.tree[2*i] + self.tree[2*i+1]
    def query(self, l, r):  # [l, r)
        res = 0
        l += self.n; r += self.n
        while l < r:
            if l & 1: res += self.tree[l]; l += 1
            if r & 1: r -= 1; res += self.tree[r]
            l //= 2; r //= 2
        return res

Q: What is Reservoir Sampling?
Write the implementation in Python.
A: Reservoir Sampling selects k items uniformly at random from a stream of unknown size in a single pass.
Time: O(n)  |  Space: O(k)
import random
def reservoir_sample(stream, k):
    reservoir = []
    for i, item in enumerate(stream):
        if i < k:
            reservoir.append(item)
        else:
            j = random.randint(0, i)
            if j < k:
                reservoir[j] = item
    return reservoir
# Each element has k/n probability of being selected

Q: How do you detect a cycle in a directed graph using DFS?
Write the implementation in Python.
A: Use 3-color marking: WHITE (unvisited), GRAY (in current path), BLACK (fully processed). A cycle exists if DFS visits a GRAY node.
Time: O(V + E)  |  Space: O(V)
def has_cycle_directed(graph, n):
    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * n
    def dfs(u):
        color[u] = GRAY
        for v in graph.get(u, []):
            if color[v] == GRAY:
                return True
            if color[v] == WHITE and dfs(v):
                return True
        color[u] = BLACK
        return False
    return any(color[u] == WHITE and dfs(u) for u in range(n))

Q: What are common Bit Manipulation tricks?
Write examples in Python.
A: Key bit operations for algorithms and competitive programming:
# Check if n is power of 2
def is_power_of_2(n):
    return n > 0 and (n & (n - 1)) == 0
# Count set bits (Brian Kernighan's)
def count_bits(n):
    count = 0
    while n:
        n &= n - 1
        count += 1
    return count
# Get/Set/Clear bit at position i
def get_bit(n, i):  return (n >> i) & 1
def set_bit(n, i):  return n | (1 << i)
def clear_bit(n, i): return n & ~(1 << i)
# XOR tricks:
# a ^ a = 0, a ^ 0 = a
# Find the single non-duplicate:
def single_number(nums):
    result = 0
    for n in nums:
        result ^= n
    return result

Q: What is the Jump Game problem (Greedy)?
Write the implementation in Python.
A: Given an array where each element is max jump length, determine if you can reach the last index. Greedy: track the farthest reachable index.
Time: O(n)  |  Space: O(1)
def can_jump(nums):
    farthest = 0
    for i in range(len(nums)):
        if i > farthest:
            return False
        farthest = max(farthest, i + nums[i])
    return True
# can_jump([2,3,1,1,4]) → True
# can_jump([3,2,1,0,4]) → False

Q: What is Level-Order Traversal (BFS on a binary tree)?
Write the implementation in Python.
A: Visit nodes level by level from left to right using a queue.
Time: O(n)  |  Space: O(n)
from collections import deque
def level_order(root):
    if not root:
        return []
    result = []
    queue = deque([root])
    while queue:
        level = []
        for _ in range(len(queue)):
            node = queue.popleft()
            level.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        result.append(level)
    return result
# Output: [[3], [9, 20], [15, 7]]

Q: How do you merge K sorted lists?
Write the implementation in Python using a min-heap.
A: Use a min-heap of size K to always pick the smallest element across all lists.
Time: O(N log K) where N = total elements  |  Space: O(K)
import heapq
def merge_k_sorted(lists):
    heap = []
    for i, lst in enumerate(lists):
        if lst:
            heapq.heappush(heap, (lst[0], i, 0))
    result = []
    while heap:
        val, list_idx, elem_idx = heapq.heappop(heap)
        result.append(val)
        if elem_idx + 1 < len(lists[list_idx]):
            next_val = lists[list_idx][elem_idx + 1]
            heapq.heappush(heap, (next_val, list_idx, elem_idx + 1))
    return result

Q: What is Binary Search on the Answer?
Write a Python example: minimum capacity to ship packages in D days.
A: When the answer is monotonic (if capacity X works, X+1 also works), binary search on the answer space instead of the array.
Time: O(n * log(sum))  |  Space: O(1)
def ship_within_days(weights, days):
    def can_ship(capacity):
        d, curr = 1, 0
        for w in weights:
            if curr + w > capacity:
                d += 1
                curr = 0
            curr += w
        return d <= days
    lo, hi = max(weights), sum(weights)
    while lo < hi:
        mid = (lo + hi) // 2
        if can_ship(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo

Q: What is the Rotate Array algorithm?
Write the O(1) space solution in Python.
A: Rotate an array to the right by k steps using the reverse trick: reverse all, reverse first k, reverse rest.
Time: O(n)  |  Space: O(1)
def rotate(nums, k):
    n = len(nums)
    k %= n
    def reverse(l, r):
        while l < r:
            nums[l], nums[r] = nums[r], nums[l]
            l += 1; r -= 1
    reverse(0, n - 1)
    reverse(0, k - 1)
    reverse(k, n - 1)
# rotate([1,2,3,4,5,6,7], 3) → [5,6,7,1,2,3,4]

Q: What is the Longest Palindromic Substring problem?
Write the expand-around-center solution in Python.
A: For each character (and each gap), expand outward while characters match. Check both odd and even length palindromes.
Time: O(n²)  |  Space: O(1)
def longest_palindrome(s):
    start, max_len = 0, 1
    def expand(l, r):
        nonlocal start, max_len
        while l >= 0 and r < len(s) and s[l] == s[r]:
            if r - l + 1 > max_len:
                start = l
                max_len = r - l + 1
            l -= 1; r += 1
    for i in range(len(s)):
        expand(i, i)      # odd length
        expand(i, i + 1)  # even length
    return s[start:start + max_len]
# longest_palindrome("babad") → "bab" or "aba"

Q: What is the Maximum Depth of a Binary Tree?
Write recursive and iterative solutions in Python.
A: Find the number of nodes along the longest path from root to leaf.
Time: O(n)  |  Space: O(h)
Recursive:
def max_depth(root):
    if not root:
        return 0
    return 1 + max(max_depth(root.left), max_depth(root.right))
Iterative (BFS):
from collections import deque
def max_depth_bfs(root):
    if not root: return 0
    depth = 0
    queue = deque([root])
    while queue:
        depth += 1
        for _ in range(len(queue)):
            node = queue.popleft()
            if node.left: queue.append(node.left)
            if node.right: queue.append(node.right)
    return depth

Q: What is the Validate BST problem?
Write the implementation in Python.
A: Check that every node's value is within valid min/max bounds defined by its ancestors.
Time: O(n)  |  Space: O(h)
def is_valid_bst(root, lo=float('-inf'), hi=float('inf')):
    if not root:
        return True
    if root.val <= lo or root.val >= hi:
        return False
    return (is_valid_bst(root.left, lo, root.val) and
            is_valid_bst(root.right, root.val, hi))

Q: What is the Diameter of a Binary Tree?
Write the implementation in Python.
A: The diameter is the longest path between any two nodes (may not pass through root). At each node, the path length is left_height + right_height.
Time: O(n)  |  Space: O(h)
def diameter_of_tree(root):
    diameter = [0]
    def height(node):
        if not node:
            return 0
        left = height(node.left)
        right = height(node.right)
        diameter[0] = max(diameter[0], left + right)
        return 1 + max(left, right)
    height(root)
    return diameter[0]

Q: What is the LRU Cache?
Write the implementation in Python.
A: Least Recently Used cache evicts the least recently accessed item when full. Uses a hash map + doubly linked list for O(1) get/put.
Get/Put: O(1)  |  Space: O(capacity)
from collections import OrderedDict
class LRUCache:
    def __init__(self, capacity):
        self.cache = OrderedDict()
        self.cap = capacity
    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)
        return self.cache[key]
    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.cap:
            self.cache.popitem(last=False)

Q: What is the Top K Frequent Elements problem?
Write the implementation in Python.
A: Find the k most frequent elements. Use a hash map for counting + heap or bucket sort.
Heap approach: O(n log k)  |  Bucket sort: O(n)
# Bucket Sort approach - O(n)
from collections import Counter
def top_k_frequent(nums, k):
    count = Counter(nums)
    buckets = [[] for _ in range(len(nums) + 1)]
    for num, freq in count.items():
        buckets[freq].append(num)
    result = []
    for i in range(len(buckets)-1, -1, -1):
        for num in buckets[i]:
            result.append(num)
            if len(result) == k:
                return result
    return result
# top_k_frequent([1,1,1,2,2,3], 2) → [1, 2]

Q: What is the Trapping Rain Water problem?
Write the two-pointer solution in Python.
A: Water trapped at each index = min(max_left, max_right) - height[i]. Two pointers avoid extra space.
Time: O(n)  |  Space: O(1)
def trap(height):
    left, right = 0, len(height) - 1
    left_max = right_max = 0
    water = 0
    while left < right:
        if height[left] < height[right]:
            if height[left] >= left_max:
                left_max = height[left]
            else:
                water += left_max - height[left]
            left += 1
        else:
            if height[right] >= right_max:
                right_max = height[right]
            else:
                water += right_max - height[right]
            right -= 1
    return water
# trap([0,1,0,2,1,0,1,3,2,1,2,1]) → 6

Q: What is the Sliding Window Maximum problem?
Write the implementation using a monotonic deque in Python.
A: Find the max in every window of size k using a monotonic decreasing deque that stores indices.
Time: O(n)  |  Space: O(k)
from collections import deque
def max_sliding_window(nums, k):
    dq = deque()  # indices of decreasing elements
    result = []
    for i in range(len(nums)):
        while dq and dq[0] < i - k + 1:
            dq.popleft()
        while dq and nums[dq[-1]] < nums[i]:
            dq.pop()
        dq.append(i)
        if i >= k - 1:
            result.append(nums[dq[0]])
    return result
# max_sliding_window([1,3,-1,-3,5,3,6,7], 3)
# → [3,3,5,5,6,7]

Q: What is the Serialize and Deserialize a Binary Tree problem?
Write the implementation in Python.
A: Convert a binary tree to a string and back. Use preorder traversal with null markers.
Time: O(n)  |  Space: O(n)
class Codec:
    def serialize(self, root):
        vals = []
        def dfs(node):
            if not node:
                vals.append('#')
                return
            vals.append(str(node.val))
            dfs(node.left)
            dfs(node.right)
        dfs(root)
        return ','.join(vals)
    def deserialize(self, data):
        vals = iter(data.split(','))
        def dfs():
            val = next(vals)
            if val == '#':
                return None
            node = TreeNode(int(val))
            node.left = dfs()
            node.right = dfs()
            return node
        return dfs()

Q: What is A* (A-Star) Search algorithm?
Write the implementation in Python.
A: A* finds the shortest path using f(n) = g(n) + h(n) where g = cost so far, h = heuristic estimate to goal. Optimal if h is admissible (never overestimates).
Time: O(E log V) with good heuristic  |  Space: O(V)
import heapq
def a_star(graph, start, goal, heuristic):
    open_set = [(heuristic(start, goal), 0, start)]
    g_score = {start: 0}
    came_from = {}
    while open_set:
        f, g, current = heapq.heappop(open_set)
        if current == goal:
            path = []
            while current in came_from:
                path.append(current)
                current = came_from[current]
            path.append(start)
            return path[::-1]
        for neighbor, weight in graph[current]:
            new_g = g + weight
            if new_g < g_score.get(neighbor, float('inf')):
                g_score[neighbor] = new_g
                came_from[neighbor] = current
                f = new_g + heuristic(neighbor, goal)
                heapq.heappush(open_set, (f, new_g, neighbor))
    return None  # no path

Q: What is the Maximum Subarray Product problem?
Write the implementation in Python.
A: Track both max and min products at each step because a negative number can flip min to max.
Time: O(n)  |  Space: O(1)
def max_product(nums):
    max_prod = min_prod = result = nums[0]
    for num in nums[1:]:
        if num < 0:
            max_prod, min_prod = min_prod, max_prod
        max_prod = max(num, max_prod * num)
        min_prod = min(num, min_prod * num)
        result = max(result, max_prod)
    return result
# max_product([2, 3, -2, 4]) → 6
# max_product([-2, 0, -1]) → 0

Q: What is the Meeting Rooms II problem (minimum rooms needed)?
Write the implementation in Python.
A: Find the maximum number of overlapping intervals using a sweep line or min-heap.
Time: O(n log n)  |  Space: O(n)
import heapq
def min_meeting_rooms(intervals):
    intervals.sort(key=lambda x: x[0])
    heap = []  # end times of ongoing meetings
    for start, end in intervals:
        if heap and heap[0] <= start:
            heapq.heappop(heap)
        heapq.heappush(heap, end)
    return len(heap)
# min_meeting_rooms([[0,30],[5,10],[15,20]]) → 2
Sweep line alternative:
def min_rooms_sweep(intervals):
    events = []
    for s, e in intervals:
        events.append((s, 1))
        events.append((e, -1))
    events.sort()
    rooms = max_rooms = 0
    for _, delta in events:
        rooms += delta
        max_rooms = max(max_rooms, rooms)
    return max_rooms

Q: What is the Graph Clone (Deep Copy) problem?
Write the implementation in Python.
A: Clone an undirected graph using BFS/DFS + hash map to map original nodes to their clones.
Time: O(V + E)  |  Space: O(V)
def clone_graph(node):
    if not node:
        return None
    clones = {node: Node(node.val)}
    queue = deque([node])
    while queue:
        curr = queue.popleft()
        for neighbor in curr.neighbors:
            if neighbor not in clones:
                clones[neighbor] = Node(neighbor.val)
                queue.append(neighbor)
            clones[curr].neighbors.append(clones[neighbor])
    return clones[node]

Q: What is the Minimum Window Substring problem?
Write the sliding window solution in Python.
A: Find the smallest substring of s that contains all characters of t (including duplicates). Use two pointers with a frequency map.
Time: O(n)  |  Space: O(k) where k = unique chars
from collections import Counter
def min_window(s, t):
    need = Counter(t)
    missing = len(t)
    left = start = 0
    min_len = float('inf')
    for right, ch in enumerate(s):
        if need[ch] > 0:
            missing -= 1
        need[ch] -= 1
        while missing == 0:
            if right - left + 1 < min_len:
                min_len = right - left + 1
                start = left
            need[s[left]] += 1
            if need[s[left]] > 0:
                missing += 1
            left += 1
    return s[start:start+min_len] if min_len != float('inf') else ""
# min_window("ADOBECODEBANC", "ABC") → "BANC"

Q: What is the Longest Substring Without Repeating Characters problem?
Write the sliding window solution in Python.
A: Use a set or dict to track characters in the current window. Shrink from left when a duplicate is found.
Time: O(n)  |  Space: O(min(n, alphabet))
def length_of_longest_substring(s):
    seen = {}
    left = max_len = 0
    for right, ch in enumerate(s):
        if ch in seen and seen[ch] >= left:
            left = seen[ch] + 1
        seen[ch] = right
        max_len = max(max_len, right - left + 1)
    return max_len
# length_of_longest_substring("abcabcbb") → 3 ("abc")

Q: What is the 3Sum problem?
Write the implementation in Python.
A: Find all unique triplets that sum to zero. Sort the array, fix one element, and use two pointers on the rest.
Time: O(n²)  |  Space: O(1) (ignoring output)
def three_sum(nums):
    nums.sort()
    result = []
    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i-1]:
            continue
        lo, hi = i + 1, len(nums) - 1
        while lo < hi:
            s = nums[i] + nums[lo] + nums[hi]
            if s == 0:
                result.append([nums[i], nums[lo], nums[hi]])
                while lo < hi and nums[lo] == nums[lo+1]: lo += 1
                while lo < hi and nums[hi] == nums[hi-1]: hi -= 1
                lo += 1; hi -= 1
            elif s < 0:
                lo += 1
            else:
                hi -= 1
    return result

Q: What is the Container With Most Water problem?
Write the two-pointer solution in Python.
A: Find two lines that together with the x-axis form a container holding the most water. Move the shorter pointer inward.
Time: O(n)  |  Space: O(1)
def max_area(height):
    left, right = 0, len(height) - 1
    max_water = 0
    while left < right:
        w = right - left
        h = min(height[left], height[right])
        max_water = max(max_water, w * h)
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
    return max_water
# max_area([1,8,6,2,5,4,8,3,7]) → 49

Q: What is the Product of Array Except Self problem?
Write the O(1) extra space solution in Python.
A: Build result using left pass (prefix products) then right pass (suffix products) without division.
Time: O(n)  |  Space: O(1) (output array not counted)
def product_except_self(nums):
    n = len(nums)
    result = [1] * n
    prefix = 1
    for i in range(n):
        result[i] = prefix
        prefix *= nums[i]
    suffix = 1
    for i in range(n - 1, -1, -1):
        result[i] *= suffix
        suffix *= nums[i]
    return result
# product_except_self([1,2,3,4]) → [24,12,8,6]

Q: What is the Valid Parentheses problem?
Write the stack solution in Python.
A: Use a stack. Push open brackets, pop on close brackets. Check the popped bracket matches.
Time: O(n)  |  Space: O(n)
def is_valid(s):
    stack = []
    pairs = {')': '(', ']': '[', '}': '{'}
    for ch in s:
        if ch in pairs:
            if not stack or stack[-1] != pairs[ch]:
                return False
            stack.pop()
        else:
            stack.append(ch)
    return len(stack) == 0
# is_valid("({[]})") → True
# is_valid("([)]") → False

Q: What is the Largest Rectangle in Histogram problem?
Write the monotonic stack solution in Python.
A: Use a monotonic increasing stack of indices. When a shorter bar is found, calculate areas for all taller bars in the stack.
Time: O(n)  |  Space: O(n)
def largest_rectangle(heights):
    stack = []
    max_area = 0
    heights.append(0)  # sentinel
    for i, h in enumerate(heights):
        while stack and heights[stack[-1]] > h:
            height = heights[stack.pop()]
            width = i if not stack else i - stack[-1] - 1
            max_area = max(max_area, height * width)
        stack.append(i)
    heights.pop()
    return max_area
# largest_rectangle([2,1,5,6,2,3]) → 10

Q: What is the Daily Temperatures problem (monotonic stack)?
Write the implementation in Python.
A: For each day, find how many days until a warmer temperature. Use a monotonic decreasing stack of indices.
Time: O(n)  |  Space: O(n)
def daily_temperatures(temps):
    n = len(temps)
    result = [0] * n
    stack = []
    for i in range(n):
        while stack and temps[stack[-1]] < temps[i]:
            j = stack.pop()
            result[j] = i - j
        stack.append(i)
    return result
# daily_temperatures([73,74,75,71,69,72,76,73])
# → [1,1,4,2,1,1,0,0]

Q: What is the Evaluate Reverse Polish Notation problem?
Write the implementation in Python.
A: Evaluate postfix expression using a stack. Push operands, pop two operands for each operator.
Time: O(n)  |  Space: O(n)
def eval_rpn(tokens):
    stack = []
    ops = {'+': lambda a,b: a+b,
           '-': lambda a,b: a-b,
           '*': lambda a,b: a*b,
           '/': lambda a,b: int(a/b)}
    for t in tokens:
        if t in ops:
            b, a = stack.pop(), stack.pop()
            stack.append(ops[t](a, b))
        else:
            stack.append(int(t))
    return stack[0]
# eval_rpn(["2","1","+","3","*"]) → 9

Q: What is the Min Stack problem?
Write the implementation in Python.
A: Design a stack that supports push, pop, top, and getMin in O(1). Store the current min alongside each element.
Time: O(1) all operations  |  Space: O(n)
class MinStack:
    def __init__(self):
        self.stack = []  # (val, current_min)
    def push(self, val):
        curr_min = min(val, self.stack[-1][1] if self.stack else val)
        self.stack.append((val, curr_min))
    def pop(self):
        self.stack.pop()
    def top(self):
        return self.stack[-1][0]
    def getMin(self):
        return self.stack[-1][1]

Q: What is the Implement Queue using Two Stacks problem?
Write the implementation in Python.
A: Use two stacks: in_stack for push, out_stack for pop/peek. Transfer elements lazily when out_stack is empty.
Amortized: O(1) per operation  |  Space: O(n)
class MyQueue:
    def __init__(self):
        self.in_stack = []
        self.out_stack = []
    def push(self, x):
        self.in_stack.append(x)
    def pop(self):
        self._move()
        return self.out_stack.pop()
    def peek(self):
        self._move()
        return self.out_stack[-1]
    def empty(self):
        return not self.in_stack and not self.out_stack
    def _move(self):
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())

Q: How do you invert a binary tree?
Write recursive and iterative solutions in Python.
A: Swap left and right children at every node.
Time: O(n)  |  Space: O(h)
Recursive:
def invert_tree(root):
    if not root:
        return None
    root.left, root.right = root.right, root.left
    invert_tree(root.left)
    invert_tree(root.right)
    return root
Iterative:
from collections import deque
def invert_tree_iter(root):
    if not root: return None
    queue = deque([root])
    while queue:
        node = queue.popleft()
        node.left, node.right = node.right, node.left
        if node.left: queue.append(node.left)
        if node.right: queue.append(node.right)
    return root

Q: How do you check if a binary tree is symmetric?
Write the implementation in Python.
A: A tree is symmetric if the left subtree is a mirror reflection of the right subtree.
Time: O(n)  |  Space: O(h)
def is_symmetric(root):
    def is_mirror(t1, t2):
        if not t1 and not t2:
            return True
        if not t1 or not t2:
            return False
        return (t1.val == t2.val and
                is_mirror(t1.left, t2.right) and
                is_mirror(t1.right, t2.left))
    return is_mirror(root, root) if root else True

Q: How do you construct a binary tree from preorder and inorder traversals?
Write the implementation in Python.
A: Preorder's first element is the root. Find it in inorder to split into left/right subtrees. Use a hash map for O(1) lookup.
Time: O(n)  |  Space: O(n)
def build_tree(preorder, inorder):
    idx_map = {val: i for i, val in enumerate(inorder)}
    pre_idx = [0]
    def build(lo, hi):
        if lo > hi:
            return None
        root_val = preorder[pre_idx[0]]
        pre_idx[0] += 1
        root = TreeNode(root_val)
        mid = idx_map[root_val]
        root.left = build(lo, mid - 1)
        root.right = build(mid + 1, hi)
        return root
    return build(0, len(inorder) - 1)

Q: What is the Kth Smallest Element in a BST?
Write the implementation in Python.
A: Do an inorder traversal (which visits BST nodes in sorted order) and return the k-th element.
Time: O(H + k)  |  Space: O(H)
def kth_smallest(root, k):
    stack = []
    curr = root
    while stack or curr:
        while curr:
            stack.append(curr)
            curr = curr.left
        curr = stack.pop()
        k -= 1
        if k == 0:
            return curr.val
        curr = curr.right

Q: What is Morris Inorder Traversal?
Write the implementation in Python.
A: Morris Traversal achieves O(1) space tree traversal by using threaded binary trees — temporarily linking rightmost node of left subtree back to current node.
Time: O(n)  |  Space: O(1)
def morris_inorder(root):
    result = []
    curr = root
    while curr:
        if not curr.left:
            result.append(curr.val)
            curr = curr.right
        else:
            pred = curr.left
            while pred.right and pred.right != curr:
                pred = pred.right
            if not pred.right:
                pred.right = curr  # create thread
                curr = curr.left
            else:
                pred.right = None  # remove thread
                result.append(curr.val)
                curr = curr.right
    return result

Q: How do you convert a sorted array to a balanced BST?
Write the implementation in Python.
A: Pick the middle element as root, recursively build left and right subtrees from the two halves.
Time: O(n)  |  Space: O(log n)
def sorted_array_to_bst(nums):
    if not nums:
        return None
    mid = len(nums) // 2
    root = TreeNode(nums[mid])
    root.left = sorted_array_to_bst(nums[:mid])
    root.right = sorted_array_to_bst(nums[mid+1:])
    return root

Q: How do you check if a binary tree is height-balanced?
Write the implementation in Python.
A: A tree is balanced if for every node, the height difference between left and right subtrees is at most 1. Use bottom-up DFS returning -1 for imbalanced.
Time: O(n)  |  Space: O(h)
def is_balanced(root):
    def check(node):
        if not node:
            return 0
        left = check(node.left)
        if left == -1: return -1
        right = check(node.right)
        if right == -1: return -1
        if abs(left - right) > 1:
            return -1
        return 1 + max(left, right)
    return check(root) != -1

Q: How do you spiral (zigzag) traverse a matrix?
Write the implementation in Python.
A: Visit elements in spiral order by shrinking boundaries: top→right→bottom→left, repeat.
Time: O(m * n)  |  Space: O(1) (ignoring output)
def spiral_order(matrix):
    result = []
    top, bottom = 0, len(matrix) - 1
    left, right = 0, len(matrix[0]) - 1
    while top <= bottom and left <= right:
        for c in range(left, right + 1):
            result.append(matrix[top][c])
        top += 1
        for r in range(top, bottom + 1):
            result.append(matrix[r][right])
        right -= 1
        if top <= bottom:
            for c in range(right, left - 1, -1):
                result.append(matrix[bottom][c])
            bottom -= 1
        if left <= right:
            for r in range(bottom, top - 1, -1):
                result.append(matrix[r][left])
            left += 1
    return result

Q: How do you rotate a matrix 90 degrees clockwise in-place?
Write the implementation in Python.
A: Transpose the matrix, then reverse each row.
Time: O(n²)  |  Space: O(1)
def rotate_matrix(matrix):
    n = len(matrix)
    # Transpose
    for i in range(n):
        for j in range(i + 1, n):
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
    # Reverse each row
    for row in matrix:
        row.reverse()
# [[1,2,3],    [[7,4,1],
#  [4,5,6], →   [8,5,2],
#  [7,8,9]]     [9,6,3]]

Q: What is the Set Matrix Zeroes problem?
Write the O(1) space solution in Python.
A: If an element is 0, set its entire row and column to 0. Use the first row and column as markers.
Time: O(m * n)  |  Space: O(1)
def set_zeroes(matrix):
    m, n = len(matrix), len(matrix[0])
    first_row_zero = any(matrix[0][j] == 0 for j in range(n))
    first_col_zero = any(matrix[i][0] == 0 for i in range(m))
    for i in range(1, m):
        for j in range(1, n):
            if matrix[i][j] == 0:
                matrix[i][0] = 0
                matrix[0][j] = 0
    for i in range(1, m):
        for j in range(1, n):
            if matrix[i][0] == 0 or matrix[0][j] == 0:
                matrix[i][j] = 0
    if first_row_zero:
        for j in range(n): matrix[0][j] = 0
    if first_col_zero:
        for i in range(m): matrix[i][0] = 0

Q: What is the Group Anagrams problem?
Write the implementation in Python.
A: Group strings that are anagrams. Use sorted characters (or character count tuple) as the hash key.
Time: O(n * k log k)  |  Space: O(n * k)
from collections import defaultdict
def group_anagrams(strs):
    groups = defaultdict(list)
    for s in strs:
        key = tuple(sorted(s))
        groups[key].append(s)
    return list(groups.values())
# group_anagrams(["eat","tea","tan","ate","nat","bat"])
# → [["eat","tea","ate"],["tan","nat"],["bat"]]

Q: What is the Unique Paths problem (DP)?
Write the implementation in Python.
A: Count paths from top-left to bottom-right of an m×n grid, moving only right or down.
Time: O(m * n)  |  Space: O(n)
def unique_paths(m, n):
    dp = [1] * n
    for i in range(1, m):
        for j in range(1, n):
            dp[j] += dp[j - 1]
    return dp[n - 1]
# unique_paths(3, 7) → 28
Also solvable with combinatorics: C(m+n-2, m-1)
from math import comb
def unique_paths_math(m, n):
    return comb(m + n - 2, m - 1)

Q: What is the Minimum Path Sum problem (DP)?
Write the implementation in Python.
A: Find the path from top-left to bottom-right with minimum sum, moving only right or down.
Time: O(m * n)  |  Space: O(n)
def min_path_sum(grid):
    m, n = len(grid), len(grid[0])
    dp = [float('inf')] * n
    dp[0] = 0
    for i in range(m):
        dp[0] += grid[i][0]
        for j in range(1, n):
            if i == 0:
                dp[j] = dp[j-1] + grid[i][j]
            else:
                dp[j] = min(dp[j], dp[j-1]) + grid[i][j]
    return dp[n-1]

Q: What is the Decode Ways problem (DP)?
Write the implementation in Python.
A: Count the number of ways to decode a digit string where 'A'=1, 'B'=2, ..., 'Z'=26. Similar to Fibonacci with constraints.
Time: O(n)  |  Space: O(1)
def num_decodings(s):
    if not s or s[0] == '0':
        return 0
    prev2, prev1 = 1, 1
    for i in range(1, len(s)):
        curr = 0
        if s[i] != '0':
            curr += prev1
        two_digit = int(s[i-1:i+1])
        if 10 <= two_digit <= 26:
            curr += prev2
        prev2, prev1 = prev1, curr
    return prev1
# num_decodings("226") → 3 ("BZ", "VF", "BBF")

Q: What is the Sieve of Eratosthenes?
Write the implementation in Python.
A: Find all prime numbers up to n by iteratively marking multiples of each prime as composite.
Time: O(n log log n)  |  Space: O(n)
def sieve(n):
    is_prime = [True] * (n + 1)
    is_prime[0] = is_prime[1] = False
    for i in range(2, int(n**0.5) + 1):
        if is_prime[i]:
            for j in range(i*i, n + 1, i):
                is_prime[j] = False
    return [i for i in range(n + 1) if is_prime[i]]
# sieve(30) → [2,3,5,7,11,13,17,19,23,29]

Q: What is the Convex Hull problem (Graham Scan)?
Write the implementation in Python.
A: Find the smallest convex polygon enclosing all given points. Graham Scan sorts by polar angle and uses a stack.
Time: O(n log n)  |  Space: O(n)
def convex_hull(points):
    def cross(O, A, B):
        return (A[0]-O[0])*(B[1]-O[1]) - (A[1]-O[1])*(B[0]-O[0])
    points = sorted(set(points))
    if len(points) <= 1:
        return points
    # Build lower hull
    lower = []
    for p in points:
        while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0:
            lower.pop()
        lower.append(p)
    # Build upper hull
    upper = []
    for p in reversed(points):
        while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0:
            upper.pop()
        upper.append(p)
    return lower[:-1] + upper[:-1]

Q: What is the Z-Algorithm for string matching?
Write the implementation in Python.
A: The Z-array stores the length of the longest substring starting at position i that matches a prefix of the string. Used for pattern matching in O(n+m).
Time: O(n)  |  Space: O(n)
def z_function(s):
    n = len(s)
    z = [0] * n
    z[0] = n
    l = r = 0
    for i in range(1, n):
        if i < r:
            z[i] = min(r - i, z[i - l])
        while i + z[i] < n and s[z[i]] == s[i + z[i]]:
            z[i] += 1
        if i + z[i] > r:
            l, r = i, i + z[i]
    return z
def z_search(text, pattern):
    combined = pattern + '$' + text
    z = z_function(combined)
    m = len(pattern)
    return [i - m - 1 for i in range(m+1, len(combined)) if z[i] == m]

Q: What is the Median of Two Sorted Arrays problem?
Write the O(log(min(m,n))) solution in Python.
A: Use binary search on the shorter array to find the correct partition where left halves ≤ right halves.
Time: O(log(min(m,n)))  |  Space: O(1)
def find_median(nums1, nums2):
    if len(nums1) > len(nums2):
        nums1, nums2 = nums2, nums1
    m, n = len(nums1), len(nums2)
    lo, hi = 0, m
    while lo <= hi:
        i = (lo + hi) // 2
        j = (m + n + 1) // 2 - i
        left1 = nums1[i-1] if i > 0 else float('-inf')
        right1 = nums1[i] if i < m else float('inf')
        left2 = nums2[j-1] if j > 0 else float('-inf')
        right2 = nums2[j] if j < n else float('inf')
        if left1 <= right2 and left2 <= right1:
            if (m + n) % 2:
                return max(left1, left2)
            return (max(left1, left2) + min(right1, right2)) / 2
        elif left1 > right2:
            hi = i - 1
        else:
            lo = i + 1

Q: What is the Palindrome Partitioning problem (backtracking + DP)?
Write the implementation in Python.
A: Partition a string so every substring is a palindrome. Use backtracking with palindrome pre-check.
Time: O(n * 2ⁿ)  |  Space: O(n)
def partition(s):
    result = []
    def is_palindrome(sub):
        return sub == sub[::-1]
    def backtrack(start, path):
        if start == len(s):
            result.append(path[:])
            return
        for end in range(start + 1, len(s) + 1):
            sub = s[start:end]
            if is_palindrome(sub):
                path.append(sub)
                backtrack(end, path)
                path.pop()
    backtrack(0, [])
    return result
# partition("aab") → [["a","a","b"],["aa","b"]]

Q: What is the Longest Valid Parentheses problem?
Write the stack solution in Python.
A: Find the length of the longest valid (well-formed) parentheses substring using a stack with index tracking.
Time: O(n)  |  Space: O(n)
def longest_valid_parentheses(s):
    stack = [-1]
    max_len = 0
    for i, ch in enumerate(s):
        if ch == '(':
            stack.append(i)
        else:
            stack.pop()
            if not stack:
                stack.append(i)
            else:
                max_len = max(max_len, i - stack[-1])
    return max_len
# longest_valid_parentheses(")()())") → 4
# longest_valid_parentheses("()(()") → 2

Q: What is the Sparse Table for Range Minimum Query?
Write the implementation in Python.
A: Sparse Table precomputes answers for power-of-2 ranges to answer min/max queries in O(1) after O(n log n) preprocessing.
Build: O(n log n)  |  Query: O(1)  |  Space: O(n log n)
import math
class SparseTable:
    def __init__(self, arr):
        n = len(arr)
        k = int(math.log2(n)) + 1 if n else 0
        self.table = [[0]*n for _ in range(k)]
        self.table[0] = arr[:]
        for j in range(1, k):
            for i in range(n - (1 << j) + 1):
                self.table[j][i] = min(
                    self.table[j-1][i],
                    self.table[j-1][i + (1 << (j-1))])
        self.log = [0] * (n + 1)
        for i in range(2, n + 1):
            self.log[i] = self.log[i//2] + 1
    def query(self, l, r):  # inclusive
        j = self.log[r - l + 1]
        return min(self.table[j][l],
                   self.table[j][r - (1 << j) + 1])

Q: What are Articulation Points (Cut Vertices) in a graph?
Write the implementation in Python.
A: An articulation point is a vertex whose removal disconnects the graph. Uses DFS with discovery time and low values.
Time: O(V + E)  |  Space: O(V)
def find_articulation_points(graph, n):
    disc = [-1] * n
    low = [-1] * n
    parent = [-1] * n
    ap = set()
    timer = [0]
    def dfs(u):
        children = 0
        disc[u] = low[u] = timer[0]
        timer[0] += 1
        for v in graph[u]:
            if disc[v] == -1:
                children += 1
                parent[v] = u
                dfs(v)
                low[u] = min(low[u], low[v])
                if parent[u] == -1 and children > 1:
                    ap.add(u)
                if parent[u] != -1 and low[v] >= disc[u]:
                    ap.add(u)
            elif v != parent[u]:
                low[u] = min(low[u], disc[v])
    for i in range(n):
        if disc[i] == -1:
            dfs(i)
    return ap

Q: What are Bridges in a graph?
Write the implementation in Python.
A: A bridge is an edge whose removal disconnects the graph. Similar to articulation points but checks low[v] > disc[u].
Time: O(V + E)  |  Space: O(V)
def find_bridges(graph, n):
    disc = [-1] * n
    low = [-1] * n
    parent = [-1] * n
    bridges = []
    timer = [0]
    def dfs(u):
        disc[u] = low[u] = timer[0]
        timer[0] += 1
        for v in graph[u]:
            if disc[v] == -1:
                parent[v] = u
                dfs(v)
                low[u] = min(low[u], low[v])
                if low[v] > disc[u]:
                    bridges.append((u, v))
            elif v != parent[u]:
                low[u] = min(low[u], disc[v])
    for i in range(n):
        if disc[i] == -1:
            dfs(i)
    return bridges

Q: What is the Edmonds-Karp (Max Flow) algorithm?
Write the implementation in Python.
A: Edmonds-Karp is BFS-based Ford-Fulkerson for maximum flow in a network. Uses BFS to find shortest augmenting paths.
Time: O(V * E²)  |  Space: O(V²)
from collections import deque
def max_flow(graph, source, sink, n):
    # graph[u][v] = capacity
    def bfs(source, sink, parent):
        visited = {source}
        queue = deque([source])
        while queue:
            u = queue.popleft()
            for v in range(n):
                if v not in visited and graph[u][v] > 0:
                    visited.add(v)
                    parent[v] = u
                    if v == sink:
                        return True
                    queue.append(v)
        return False
    total_flow = 0
    parent = [-1] * n
    while bfs(source, sink, parent):
        path_flow = float('inf')
        v = sink
        while v != source:
            u = parent[v]
            path_flow = min(path_flow, graph[u][v])
            v = u
        v = sink
        while v != source:
            u = parent[v]
            graph[u][v] -= path_flow
            graph[v][u] += path_flow
            v = u
        total_flow += path_flow
        parent = [-1] * n
    return total_flow

Q: What is Matrix Exponentiation and when is it used?
Write the implementation in Python.
A: Compute the n-th power of a matrix in O(k³ log n) using binary exponentiation. Used to solve linear recurrences (e.g., Fibonacci) in O(log n).
Time: O(k³ log n)  |  Space: O(k²)
def mat_mult(A, B, mod=10**9+7):
    n = len(A)
    C = [[0]*n for _ in range(n)]
    for i in range(n):
        for j in range(n):
            for k in range(n):
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod
    return C
def mat_pow(M, p, mod=10**9+7):
    n = len(M)
    result = [[int(i==j) for j in range(n)] for i in range(n)]
    while p:
        if p & 1:
            result = mat_mult(result, M, mod)
        M = mat_mult(M, M, mod)
        p >>= 1
    return result
# Fibonacci in O(log n):
# [[1,1],[1,0]]^n → fib(n) at [0][1]
def fib_fast(n):
    if n <= 0: return 0
    M = [[1,1],[1,0]]
    return mat_pow(M, n)[0][1]

Q: What is the Longest Repeating Character Replacement problem?
Write the sliding window solution in Python.
A: Find the length of the longest substring with the same letter after at most k replacements. Window is valid when window_size - max_freq ≤ k.
Time: O(n)  |  Space: O(1)
def character_replacement(s, k):
    count = {}
    left = max_freq = result = 0
    for right in range(len(s)):
        count[s[right]] = count.get(s[right], 0) + 1
        max_freq = max(max_freq, count[s[right]])
        if (right - left + 1) - max_freq > k:
            count[s[left]] -= 1
            left += 1
        result = max(result, right - left + 1)
    return result
# character_replacement("AABABBA", 1) → 4 ("AABA")

Q: What is the Subarray Sum Equals K problem?
Write the prefix sum + hash map solution in Python.
A: Count subarrays summing to k. Track prefix sums in a hash map; if prefix[j] - prefix[i] == k, subarray (i,j] sums to k.
Time: O(n)  |  Space: O(n)
def subarray_sum(nums, k):
    count = 0
    prefix = 0
    seen = {0: 1}
    for num in nums:
        prefix += num
        if prefix - k in seen:
            count += seen[prefix - k]
        seen[prefix] = seen.get(prefix, 0) + 1
    return count
# subarray_sum([1,2,3], 3) → 2  ([1,2] and [3])

Q: What is the Task Scheduler problem?
Write the implementation in Python.
A: Schedule tasks with a cooldown of n between same tasks. The answer depends on the most frequent task.
Time: O(N)  |  Space: O(1)
from collections import Counter
def least_interval(tasks, n):
    freq = Counter(tasks)
    max_freq = max(freq.values())
    max_count = sum(1 for v in freq.values() if v == max_freq)
    # (max_freq-1) full chunks of (n+1), plus final partial chunk
    result = (max_freq - 1) * (n + 1) + max_count
    return max(result, len(tasks))
# least_interval(["A","A","A","B","B","B"], 2) → 8
# A B _ A B _ A B

Q: What is the Course Schedule problem (can you finish all courses)?
Write the implementation in Python.
A: Detect if a cycle exists in a directed graph. If no cycle, all courses can be finished (topological ordering exists).
Time: O(V + E)  |  Space: O(V)
from collections import deque
def can_finish(num_courses, prerequisites):
    graph = [[] for _ in range(num_courses)]
    in_degree = [0] * num_courses
    for course, prereq in prerequisites:
        graph[prereq].append(course)
        in_degree[course] += 1
    queue = deque(i for i in range(num_courses) if in_degree[i] == 0)
    count = 0
    while queue:
        node = queue.popleft()
        count += 1
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)
    return count == num_courses

Q: What is the Word Search problem (backtracking on a grid)?
Write the implementation in Python.
A: Search for a word in a 2D grid by moving to adjacent cells (up/down/left/right) without reusing cells.
Time: O(m * n * 4^L)  |  Space: O(L)
def exist(board, word):
    rows, cols = len(board), len(board[0])
    def dfs(r, c, idx):
        if idx == len(word):
            return True
        if (r < 0 or r >= rows or c < 0 or c >= cols
                or board[r][c] != word[idx]):
            return False
        temp = board[r][c]
        board[r][c] = '#'
        found = any(dfs(r+dr, c+dc, idx+1)
            for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)])
        board[r][c] = temp
        return found
    return any(dfs(r, c, 0)
        for r in range(rows) for c in range(cols))

Q: What is the Alien Dictionary problem (topological sort on characters)?
Write the implementation in Python.
A: Given a sorted list of words in an alien language, determine the character ordering using topological sort.
Time: O(C) where C = total chars  |  Space: O(U) where U = unique chars
from collections import deque, defaultdict
def alien_order(words):
    graph = defaultdict(set)
    in_degree = {ch: 0 for word in words for ch in word}
    for i in range(len(words) - 1):
        w1, w2 = words[i], words[i+1]
        min_len = min(len(w1), len(w2))
        if len(w1) > len(w2) and w1[:min_len] == w2[:min_len]:
            return ""  # invalid
        for j in range(min_len):
            if w1[j] != w2[j]:
                if w2[j] not in graph[w1[j]]:
                    graph[w1[j]].add(w2[j])
                    in_degree[w2[j]] += 1
                break
    queue = deque(ch for ch in in_degree if in_degree[ch] == 0)
    order = []
    while queue:
        ch = queue.popleft()
        order.append(ch)
        for nxt in graph[ch]:
            in_degree[nxt] -= 1
            if in_degree[nxt] == 0:
                queue.append(nxt)
    return "".join(order) if len(order) == len(in_degree) else ""

Q: What is the Shortest Path in Binary Matrix (BFS)?
Write the implementation in Python.
A: Find the shortest path from top-left to bottom-right in a binary grid, moving in 8 directions. BFS guarantees shortest path in unweighted graphs.
Time: O(n²)  |  Space: O(n²)
from collections import deque
def shortest_path(grid):
    n = len(grid)
    if grid[0][0] or grid[n-1][n-1]:
        return -1
    queue = deque([(0, 0, 1)])
    grid[0][0] = 1
    dirs = [(-1,-1),(-1,0),(-1,1),(0,-1),
            (0,1),(1,-1),(1,0),(1,1)]
    while queue:
        r, c, dist = queue.popleft()
        if r == n-1 and c == n-1:
            return dist
        for dr, dc in dirs:
            nr, nc = r+dr, c+dc
            if 0 <= nr < n and 0 <= nc < n and not grid[nr][nc]:
                grid[nr][nc] = 1
                queue.append((nr, nc, dist+1))
    return -1

Q: What is the Pacific Atlantic Water Flow problem?
Write the implementation in Python.
A: Find cells where water can flow to both oceans. Do reverse BFS/DFS from each ocean border inward.
Time: O(m * n)  |  Space: O(m * n)
from collections import deque
def pacific_atlantic(heights):
    if not heights: return []
    m, n = len(heights), len(heights[0])
    def bfs(starts):
        reachable = set(starts)
        queue = deque(starts)
        while queue:
            r, c = queue.popleft()
            for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
                nr, nc = r+dr, c+dc
                if (0<=nr                        and (nr,nc) not in reachable
                        and heights[nr][nc] >= heights[r][c]):
                    reachable.add((nr,nc))
                    queue.append((nr,nc))
        return reachable
    pacific = [(r,0) for r in range(m)] + [(0,c) for c in range(1,n)]
    atlantic = [(r,n-1) for r in range(m)] + [(m-1,c) for c in range(n-1)]
    return list(bfs(pacific) & bfs(atlantic))

Q: What is the Surrounded Regions problem?
Write the implementation in Python.
A: Capture all 'O' regions not connected to the border. DFS/BFS from border 'O's to mark safe cells, then flip the rest.
Time: O(m * n)  |  Space: O(m * n)
def solve(board):
    if not board: return
    m, n = len(board), len(board[0])
    def dfs(r, c):
        if r < 0 or r >= m or c < 0 or c >= n or board[r][c] != 'O':
            return
        board[r][c] = 'S'  # safe
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            dfs(r+dr, c+dc)
    # Mark border-connected O's as safe
    for r in range(m):
        dfs(r, 0); dfs(r, n-1)
    for c in range(n):
        dfs(0, c); dfs(m-1, c)
    for r in range(m):
        for c in range(n):
            if board[r][c] == 'O': board[r][c] = 'X'
            elif board[r][c] == 'S': board[r][c] = 'O'

Q: What is the Rotting Oranges problem (multi-source BFS)?
Write the implementation in Python.
A: All rotten oranges spread simultaneously. Use multi-source BFS starting from all rotten oranges at once.
Time: O(m * n)  |  Space: O(m * n)
from collections import deque
def oranges_rotting(grid):
    m, n = len(grid), len(grid[0])
    queue = deque()
    fresh = 0
    for r in range(m):
        for c in range(n):
            if grid[r][c] == 2:
                queue.append((r, c, 0))
            elif grid[r][c] == 1:
                fresh += 1
    if fresh == 0: return 0
    minutes = 0
    while queue:
        r, c, t = queue.popleft()
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            nr, nc = r+dr, c+dc
            if 0<=nr                grid[nr][nc] = 2
                fresh -= 1
                minutes = t + 1
                queue.append((nr, nc, t+1))
    return minutes if fresh == 0 else -1

Q: What is the Reconstruct Itinerary problem (Eulerian Path)?
Write the implementation in Python.
A: Find an itinerary using all tickets exactly once. This is an Eulerian path — use Hierholzer's algorithm with DFS.
Time: O(E log E)  |  Space: O(E)
from collections import defaultdict
def find_itinerary(tickets):
    graph = defaultdict(list)
    for src, dst in sorted(tickets, reverse=True):
        graph[src].append(dst)
    route = []
    def dfs(airport):
        while graph[airport]:
            dfs(graph[airport].pop())
        route.append(airport)
    dfs("JFK")
    return route[::-1]
# find_itinerary([["JFK","SFO"],["JFK","ATL"],
#   ["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]])
# → ["JFK","ATL","JFK","SFO","ATL","SFO"]

Q: What is the Design Twitter (OOP + Merge K Sorted) problem?
Write the implementation in Python.
A: Combine hash maps for follow/unfollow and a heap to merge recent tweets from followed users.
import heapq
from collections import defaultdict
class Twitter:
    def __init__(self):
        self.time = 0
        self.tweets = defaultdict(list)   # userId → [(time, tweetId)]
        self.follows = defaultdict(set)   # userId → {followeeIds}
    def postTweet(self, userId, tweetId):
        self.tweets[userId].append((self.time, tweetId))
        self.time -= 1  # negative for max-heap via min-heap
    def getNewsFeed(self, userId):
        heap = []
        self.follows[userId].add(userId)
        for uid in self.follows[userId]:
            if self.tweets[uid]:
                idx = len(self.tweets[uid]) - 1
                t, tid = self.tweets[uid][idx]
                heapq.heappush(heap, (t, tid, uid, idx))
        feed = []
        while heap and len(feed) < 10:
            t, tid, uid, idx = heapq.heappop(heap)
            feed.append(tid)
            if idx > 0:
                t2, tid2 = self.tweets[uid][idx-1]
                heapq.heappush(heap, (t2, tid2, uid, idx-1))
        return feed
    def follow(self, followerId, followeeId):
        self.follows[followerId].add(followeeId)
    def unfollow(self, followerId, followeeId):
        self.follows[followerId].discard(followeeId)

Q: What is the Maximal Rectangle in a binary matrix?
Write the implementation in Python.
A: Build a histogram per row and apply the Largest Rectangle in Histogram algorithm to each.
Time: O(m * n)  |  Space: O(n)
def maximal_rectangle(matrix):
    if not matrix: return 0
    n = len(matrix[0])
    heights = [0] * (n + 1)
    max_area = 0
    for row in matrix:
        for j in range(n):
            heights[j] = heights[j] + 1 if row[j] == '1' else 0
        # Largest rectangle in histogram
        stack = []
        for i in range(n + 1):
            while stack and heights[stack[-1]] > heights[i]:
                h = heights[stack.pop()]
                w = i if not stack else i - stack[-1] - 1
                max_area = max(max_area, h * w)
            stack.append(i)
    return max_area

Q: What is the Longest Consecutive Sequence problem?
Write the O(n) solution in Python.
A: Find the length of the longest sequence of consecutive integers. Use a hash set and only start counting from sequence beginnings.
Time: O(n)  |  Space: O(n)
def longest_consecutive(nums):
    num_set = set(nums)
    best = 0
    for n in num_set:
        if n - 1 not in num_set:  # start of a sequence
            length = 1
            while n + length in num_set:
                length += 1
            best = max(best, length)
    return best
# longest_consecutive([100,4,200,1,3,2]) → 4 (1,2,3,4)

Q: What is the Partition Equal Subset Sum problem (DP)?
Write the implementation in Python.
A: Determine if array can be partitioned into two subsets with equal sum. Reduces to 0/1 knapsack with target = total/2.
Time: O(n * sum)  |  Space: O(sum)
def can_partition(nums):
    total = sum(nums)
    if total % 2:
        return False
    target = total // 2
    dp = [False] * (target + 1)
    dp[0] = True
    for num in nums:
        for j in range(target, num - 1, -1):
            dp[j] = dp[j] or dp[j - num]
    return dp[target]
# can_partition([1,5,11,5]) → True (1+5+5=11)

Q: What is the Target Sum problem (DP)?
Write the implementation in Python.
A: Assign + or - to each number to reach a target. Transform to subset sum: find subsets summing to (total + target) / 2.
Time: O(n * sum)  |  Space: O(sum)
def find_target_sum_ways(nums, target):
    total = sum(nums)
    if (total + target) % 2 or abs(target) > total:
        return 0
    subset_sum = (total + target) // 2
    dp = [0] * (subset_sum + 1)
    dp[0] = 1
    for num in nums:
        for j in range(subset_sum, num - 1, -1):
            dp[j] += dp[j - num]
    return dp[subset_sum]
# find_target_sum_ways([1,1,1,1,1], 3) → 5

Q: What is the Maximal Square problem (DP)?
Write the implementation in Python.
A: Find the largest square of '1's in a binary matrix. dp[i][j] = side length of largest square ending at (i,j).
Time: O(m * n)  |  Space: O(n)
def maximal_square(matrix):
    if not matrix: return 0
    m, n = len(matrix), len(matrix[0])
    dp = [0] * (n + 1)
    max_side = 0
    prev = 0
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            temp = dp[j]
            if matrix[i-1][j-1] == '1':
                dp[j] = min(dp[j], dp[j-1], prev) + 1
                max_side = max(max_side, dp[j])
            else:
                dp[j] = 0
            prev = temp
    return max_side * max_side

Q: What is the Word Ladder problem (BFS)?
Write the implementation in Python.
A: Find the shortest transformation sequence from beginWord to endWord, changing one letter at a time, using only dictionary words.
Time: O(n * m * 26)  |  Space: O(n * m)
from collections import deque
def ladder_length(begin, end, word_list):
    word_set = set(word_list)
    if end not in word_set:
        return 0
    queue = deque([(begin, 1)])
    visited = {begin}
    while queue:
        word, steps = queue.popleft()
        for i in range(len(word)):
            for c in 'abcdefghijklmnopqrstuvwxyz':
                next_word = word[:i] + c + word[i+1:]
                if next_word == end:
                    return steps + 1
                if next_word in word_set and next_word not in visited:
                    visited.add(next_word)
                    queue.append((next_word, steps + 1))
    return 0

Q: What is the Minimum Window Sort problem?
Write the implementation in Python.
A: Find the shortest subarray that, if sorted, makes the entire array sorted. Find out-of-order boundaries from both ends.
Time: O(n)  |  Space: O(1)
def find_unsorted_subarray(nums):
    n = len(nums)
    lo, hi = -1, -2  # default: empty range
    min_val = nums[-1]
    max_val = nums[0]
    for i in range(1, n):
        max_val = max(max_val, nums[i])
        min_val = min(min_val, nums[n-1-i])
        if nums[i] < max_val:
            hi = i
        if nums[n-1-i] > min_val:
            lo = n - 1 - i
    return hi - lo + 1
# find_unsorted_subarray([2,6,4,8,10,9,15]) → 5

Q: What is the Find All Anagrams in a String problem?
Write the sliding window solution in Python.
A: Find all start indices of p's anagrams in s using a fixed-size sliding window with character counts.
Time: O(n)  |  Space: O(1)
from collections import Counter
def find_anagrams(s, p):
    if len(p) > len(s): return []
    p_count = Counter(p)
    s_count = Counter(s[:len(p)])
    result = []
    if s_count == p_count:
        result.append(0)
    for i in range(len(p), len(s)):
        s_count[s[i]] += 1
        left = s[i - len(p)]
        s_count[left] -= 1
        if s_count[left] == 0:
            del s_count[left]
        if s_count == p_count:
            result.append(i - len(p) + 1)
    return result
# find_anagrams("cbaebabacd", "abc") → [0, 6]

Q: What is the Sudoku Solver (backtracking)?
Write the implementation in Python.
A: Fill empty cells by trying digits 1-9, backtracking when a conflict is found. Validate row, column, and 3×3 box.
Time: O(9^(empty cells)) worst  |  Space: O(81)
def solve_sudoku(board):
    def is_valid(r, c, ch):
        for i in range(9):
            if board[r][i] == ch: return False
            if board[i][c] == ch: return False
            br, bc = 3*(r//3) + i//3, 3*(c//3) + i%3
            if board[br][bc] == ch: return False
        return True
    def solve():
        for r in range(9):
            for c in range(9):
                if board[r][c] == '.':
                    for ch in '123456789':
                        if is_valid(r, c, ch):
                            board[r][c] = ch
                            if solve():
                                return True
                            board[r][c] = '.'
                    return False
        return True
    solve()

Q: What is the Combination Sum problem (backtracking)?
Write the implementation in Python.
A: Find all unique combinations where candidates sum to target. Each number can be used unlimited times.
Time: O(n^(T/M))  |  Space: O(T/M)
def combination_sum(candidates, target):
    result = []
    def backtrack(start, path, remaining):
        if remaining == 0:
            result.append(path[:])
            return
        for i in range(start, len(candidates)):
            if candidates[i] > remaining:
                break
            path.append(candidates[i])
            backtrack(i, path, remaining - candidates[i])
            path.pop()
    candidates.sort()
    backtrack(0, [], target)
    return result
# combination_sum([2,3,6,7], 7) → [[2,2,3],[7]]

Q: What is the Regular Expression Matching problem (DP)?
Write the implementation in Python.
A: Match string against pattern with '.' (any char) and '*' (zero or more of preceding). Use 2D DP.
Time: O(m * n)  |  Space: O(m * n)
def is_match(s, p):
    m, n = len(s), len(p)
    dp = [[False]*(n+1) for _ in range(m+1)]
    dp[0][0] = True
    for j in range(1, n+1):
        if p[j-1] == '*':
            dp[0][j] = dp[0][j-2]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if p[j-1] == '*':
                dp[i][j] = dp[i][j-2]  # zero occurrences
                if p[j-2] == '.' or p[j-2] == s[i-1]:
                    dp[i][j] |= dp[i-1][j]  # one+ occurrences
            elif p[j-1] == '.' or p[j-1] == s[i-1]:
                dp[i][j] = dp[i-1][j-1]
    return dp[m][n]
# is_match("aab", "c*a*b") → True

Q: What is the Gas Station problem (Greedy)?
Write the implementation in Python.
A: Find the starting gas station index to complete a circular tour. If total gas ≥ total cost, a solution exists. Reset start when tank goes negative.
Time: O(n)  |  Space: O(1)
def can_complete_circuit(gas, cost):
    total_tank = curr_tank = 0
    start = 0
    for i in range(len(gas)):
        diff = gas[i] - cost[i]
        total_tank += diff
        curr_tank += diff
        if curr_tank < 0:
            start = i + 1
            curr_tank = 0
    return start if total_tank >= 0 else -1
# can_complete_circuit([1,2,3,4,5],[3,4,5,1,2]) → 3

Q: What is the Candy problem (Greedy)?
Write the implementation in Python.
A: Distribute candies so each child gets at least 1 and children with higher ratings get more than neighbors. Two passes: left-to-right and right-to-left.
Time: O(n)  |  Space: O(n)
def candy(ratings):
    n = len(ratings)
    candies = [1] * n
    # Left to right
    for i in range(1, n):
        if ratings[i] > ratings[i-1]:
            candies[i] = candies[i-1] + 1
    # 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)
# candy([1,0,2]) → 5 (2+1+2)

Q: What is the Find Median from Data Stream problem?
Write the implementation using two heaps in Python.
A: Maintain a max-heap for the lower half and a min-heap for the upper half. Balance sizes after each insertion.
Add: O(log n)  |  FindMedian: O(1)
import heapq
class MedianFinder:
    def __init__(self):
        self.lo = []  # max-heap (negated)
        self.hi = []  # min-heap
    def addNum(self, num):
        heapq.heappush(self.lo, -num)
        heapq.heappush(self.hi, -heapq.heappop(self.lo))
        if len(self.hi) > len(self.lo):
            heapq.heappush(self.lo, -heapq.heappop(self.hi))
    def findMedian(self):
        if len(self.lo) > len(self.hi):
            return -self.lo[0]
        return (-self.lo[0] + self.hi[0]) / 2

Q: What is the Interval List Intersections problem?
Write the two-pointer solution in Python.
A: Find intersections of two sorted interval lists. Intersection exists when max(starts) ≤ min(ends). Advance the pointer with the smaller end.
Time: O(m + n)  |  Space: O(1)
def interval_intersection(A, B):
    i = j = 0
    result = []
    while i < len(A) and j < len(B):
        lo = max(A[i][0], B[j][0])
        hi = min(A[i][1], B[j][1])
        if lo <= hi:
            result.append([lo, hi])
        if A[i][1] < B[j][1]:
            i += 1
        else:
            j += 1
    return result

Q: What is the Longest Palindromic Subsequence problem (DP)?
Write the implementation in Python.
A: Find the longest subsequence that is a palindrome. Equivalent to LCS of the string and its reverse.
Time: O(n²)  |  Space: O(n)
def longest_palindrome_subseq(s):
    n = len(s)
    dp = [0] * n
    for i in range(n - 1, -1, -1):
        new_dp = [0] * n
        new_dp[i] = 1
        for j in range(i + 1, n):
            if s[i] == s[j]:
                new_dp[j] = dp[j - 1] + 2
            else:
                new_dp[j] = max(dp[j], new_dp[j - 1])
        dp = new_dp
    return dp[n - 1]
# longest_palindrome_subseq("bbbab") → 4 ("bbbb")

Q: What is the Implement a Hash Map problem?
Write a simple implementation in Python.
A: Use an array of buckets with chaining (linked lists or arrays). Hash = key % bucket_count.
Get/Put/Remove: O(1) avg  |  Space: O(n)
class MyHashMap:
    def __init__(self):
        self.size = 1000
        self.buckets = [[] for _ in range(self.size)]
    def _hash(self, key):
        return key % self.size
    def put(self, key, value):
        bucket = self.buckets[self._hash(key)]
        for i, (k, v) in enumerate(bucket):
            if k == key:
                bucket[i] = (key, value)
                return
        bucket.append((key, value))
    def get(self, key):
        for k, v in self.buckets[self._hash(key)]:
            if k == key:
                return v
        return -1
    def remove(self, key):
        bucket = self.buckets[self._hash(key)]
        for i, (k, v) in enumerate(bucket):
            if k == key:
                bucket.pop(i)
                return

Q: What is the String Compression (Run-Length Encoding) problem?
Write the in-place implementation in Python.
A: Compress consecutive repeated characters in-place. Write char followed by count (if > 1).
Time: O(n)  |  Space: O(1)
def compress(chars):
    write = read = 0
    while read < len(chars):
        ch = chars[read]
        count = 0
        while read < len(chars) and chars[read] == ch:
            read += 1
            count += 1
        chars[write] = ch
        write += 1
        if count > 1:
            for digit in str(count):
                chars[write] = digit
                write += 1
    return write
# ['a','a','b','b','c','c','c'] → ['a','2','b','2','c','3']

Q: What is the Maximum Profit in Job Scheduling problem (DP + Binary Search)?
Write the implementation in Python.
A: Schedule non-overlapping jobs for max profit. Sort by end time, use DP + binary search to find latest non-conflicting job.
Time: O(n log n)  |  Space: O(n)
from bisect import bisect_right
def job_scheduling(start, end, profit):
    jobs = sorted(zip(end, start, profit))
    ends = [j[0] for j in jobs]
    n = len(jobs)
    dp = [0] * (n + 1)
    for i in range(1, n + 1):
        e, s, p = jobs[i-1]
        # Find latest job ending <= start of current job
        k = bisect_right(ends, s, 0, i)
        dp[i] = max(dp[i-1], dp[k] + p)
    return dp[n]
# job_scheduling([1,2,3,3],[3,4,5,6],[50,10,40,70]) → 120

Q: What is the Topological Sort using DFS (post-order)?
Write the implementation in Python.
A: Alternative to Kahn's BFS: run DFS and add nodes to result in reverse post-order (append after all descendants are visited).
Time: O(V + E)  |  Space: O(V)
def topo_sort_dfs(graph, n):
    visited = [False] * n
    stack = []
    def dfs(u):
        visited[u] = True
        for v in graph.get(u, []):
            if not visited[v]:
                dfs(v)
        stack.append(u)
    for i in range(n):
        if not visited[i]:
            dfs(i)
    return stack[::-1]

Q: What is the Minimum Spanning Tree weight using Kruskal's with path compression?
Explain the complete workflow.
A: 1. Sort all edges by weight
2. For each edge, if it connects two different components (Union-Find), add it to MST
3. Stop when MST has V-1 edges
Key insight: Greedy choice of cheapest edge that doesn't form a cycle is always optimal.
# Full example with Union-Find
edges = [(1,'A','B'),(2,'B','C'),(3,'A','C'),(4,'C','D')]
edges.sort()  # by weight
uf = UnionFind(4)
mst_weight = 0
for w, u, v in edges:
    if uf.find(u) != uf.find(v):
        uf.union(u, v)
        mst_weight += w
# Picks edges 1,2,4 → weight = 7

Q: What is the Minimum Cost to Connect All Points problem?
Write the implementation using Prim's in Python.
A: Given points on a 2D plane, find min cost to connect all points where cost = Manhattan distance. Use Prim's with a min-heap.
Time: O(n² log n)  |  Space: O(n²)
import heapq
def min_cost_connect_points(points):
    n = len(points)
    visited = [False] * n
    heap = [(0, 0)]  # (cost, point_idx)
    total = 0
    edges_used = 0
    while edges_used < n:
        cost, u = heapq.heappop(heap)
        if visited[u]:
            continue
        visited[u] = True
        total += cost
        edges_used += 1
        for v in range(n):
            if not visited[v]:
                dist = (abs(points[u][0]-points[v][0])
                      + abs(points[u][1]-points[v][1]))
                heapq.heappush(heap, (dist, v))
    return total

Q: What is the Network Delay Time problem (Dijkstra application)?
Write the implementation in Python.
A: Find the time for a signal to reach all nodes from source. Run Dijkstra and return the max distance. If any node is unreachable, return -1.
Time: O(E log V)  |  Space: O(V + E)
import heapq
from collections import defaultdict
def network_delay(times, n, k):
    graph = defaultdict(list)
    for u, v, w in times:
        graph[u].append((v, w))
    dist = {k: 0}
    heap = [(0, k)]
    while heap:
        d, u = heapq.heappop(heap)
        if d > dist.get(u, float('inf')):
            continue
        for v, w in graph[u]:
            if d + w < dist.get(v, float('inf')):
                dist[v] = d + w
                heapq.heappush(heap, (d + w, v))
    return max(dist.values()) if len(dist) == n else -1

Q: What is the Cheapest Flights Within K Stops problem?
Write the Bellman-Ford variant in Python.
A: Find cheapest flight with at most k stops. Run Bellman-Ford for exactly k+1 iterations, using a copy of distances each round to prevent cascading updates.
Time: O(k * E)  |  Space: O(V)
def find_cheapest_price(n, flights, src, dst, k):
    dist = [float('inf')] * n
    dist[src] = 0
    for _ in range(k + 1):
        tmp = dist[:]
        for u, v, w in flights:
            if dist[u] + w < tmp[v]:
                tmp[v] = dist[u] + w
        dist = tmp
    return dist[dst] if dist[dst] != float('inf') else -1

Q: What is the Redundant Connection problem (Union-Find)?
Write the implementation in Python.
A: Find the edge that, when removed, makes the graph a tree (no cycles). The first edge that connects two already-connected nodes is redundant.
Time: O(n * α(n)) ≈ O(n)  |  Space: O(n)
def find_redundant(edges):
    parent = list(range(len(edges) + 1))
    rank = [0] * (len(edges) + 1)
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x
    def union(x, y):
        px, py = find(x), find(y)
        if px == py: return False
        if rank[px] < rank[py]: px, py = py, px
        parent[py] = px
        if rank[px] == rank[py]: rank[px] += 1
        return True
    for u, v in edges:
        if not union(u, v):
            return [u, v]

Q: What is the Accounts Merge problem (Union-Find)?
Write the implementation in Python.
A: Merge accounts sharing the same email. Use Union-Find on email→account_id mapping.
Time: O(n * k * α(n))  |  Space: O(n * k)
from collections import defaultdict
def accounts_merge(accounts):
    parent = {}
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x
    def union(x, y):
        parent[find(x)] = find(y)
    email_to_name = {}
    for acct in accounts:
        name = acct[0]
        for email in acct[1:]:
            if email not in parent:
                parent[email] = email
            email_to_name[email] = name
            union(email, acct[1])
    groups = defaultdict(list)
    for email in parent:
        groups[find(email)].append(email)
    return [[email_to_name[root]] + sorted(emails)
            for root, emails in groups.items()]

Q: What is the Swim in Rising Water problem (binary search + BFS)?
Write the implementation in Python.
A: Find the minimum time t such that you can swim from top-left to bottom-right where all cells on the path have elevation ≤ t. Use min-heap (modified Dijkstra).
Time: O(n² log n)  |  Space: O(n²)
import heapq
def swim_in_water(grid):
    n = len(grid)
    visited = [[False]*n for _ in range(n)]
    heap = [(grid[0][0], 0, 0)]
    visited[0][0] = True
    while heap:
        t, r, c = heapq.heappop(heap)
        if r == n-1 and c == n-1:
            return t
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            nr, nc = r+dr, c+dc
            if 0<=nr                visited[nr][nc] = True
                heapq.heappush(heap, (max(t, grid[nr][nc]), nr, nc))
    return -1

Q: What is the Longest String Chain problem (DP)?
Write the implementation in Python.
A: A word chain where each word differs from the previous by exactly one added letter. Sort by length, DP on predecessors.
Time: O(n * L²)  |  Space: O(n)
def longest_str_chain(words):
    words.sort(key=len)
    dp = {}
    best = 1
    for word in words:
        dp[word] = 1
        for i in range(len(word)):
            prev = word[:i] + word[i+1:]
            if prev in dp:
                dp[word] = max(dp[word], dp[prev] + 1)
        best = max(best, dp[word])
    return best
# longest_str_chain(["a","b","ba","bca","bda","bdca"]) → 4
# "a" → "ba" → "bda" → "bdca"

Q: What is the Distinct Subsequences problem (DP)?
Write the implementation in Python.
A: Count the number of distinct subsequences of s that equal t.
Time: O(m * n)  |  Space: O(n)
def num_distinct(s, t):
    m, n = len(s), len(t)
    dp = [0] * (n + 1)
    dp[0] = 1
    for i in range(1, m + 1):
        for j in range(n, 0, -1):
            if s[i-1] == t[j-1]:
                dp[j] += dp[j-1]
    return dp[n]
# num_distinct("rabbbit", "rabbit") → 3

Q: What is the Burst Balloons problem (interval DP)?
Write the implementation in Python.
A: Burst balloons to maximize coins. dp[i][j] = max coins from bursting all balloons between i and j. Think of the last balloon to burst in each range.
Time: O(n³)  |  Space: O(n²)
def max_coins(nums):
    nums = [1] + nums + [1]
    n = len(nums)
    dp = [[0]*n for _ in range(n)]
    for length in range(2, n):
        for left in range(0, n - length):
            right = left + length
            for k in range(left+1, right):
                dp[left][right] = max(dp[left][right],
                    dp[left][k] + dp[k][right]
                    + nums[left]*nums[k]*nums[right])
    return dp[0][n-1]
# max_coins([3,1,5,8]) → 167

Q: What is the Wildcard Matching problem (DP)?
Write the implementation in Python.
A: Match string against pattern with '?' (any single char) and '*' (any sequence including empty). Use 2D DP.
Time: O(m * n)  |  Space: O(n)
def is_match(s, p):
    m, n = len(s), len(p)
    dp = [False] * (n + 1)
    dp[0] = True
    for j in range(1, n + 1):
        dp[j] = dp[j-1] and p[j-1] == '*'
    for i in range(1, m + 1):
        new_dp = [False] * (n + 1)
        for j in range(1, n + 1):
            if p[j-1] == '*':
                new_dp[j] = new_dp[j-1] or dp[j]
            elif p[j-1] == '?' or p[j-1] == s[i-1]:
                new_dp[j] = dp[j-1]
        dp = new_dp
    return dp[n]
# is_match("adceb", "*a*b") → True

Q: What is the Interleaving String problem (DP)?
Write the implementation in Python.
A: Check if s3 is an interleaving of s1 and s2 (characters maintain relative order from both strings).
Time: O(m * n)  |  Space: O(n)
def is_interleave(s1, s2, s3):
    m, n = len(s1), len(s2)
    if m + n != len(s3):
        return False
    dp = [False] * (n + 1)
    dp[0] = True
    for j in range(1, n + 1):
        dp[j] = dp[j-1] and s2[j-1] == s3[j-1]
    for i in range(1, m + 1):
        dp[0] = dp[0] and s1[i-1] == s3[i-1]
        for j in range(1, n + 1):
            dp[j] = ((dp[j] and s1[i-1] == s3[i+j-1]) or
                     (dp[j-1] and s2[j-1] == s3[i+j-1]))
    return dp[n]

Q: What is the Minimum Number of Platforms problem?
Write the implementation in Python.
A: Find the minimum number of platforms needed at a train station so no train waits. Same as maximum overlap of intervals.
Time: O(n log n)  |  Space: O(n)
def min_platforms(arrivals, departures):
    events = []
    for a in arrivals:
        events.append((a, 1))
    for d in departures:
        events.append((d + 0.5, -1))  # depart after arrive at same time
    events.sort()
    curr = max_platforms = 0
    for _, delta in events:
        curr += delta
        max_platforms = max(max_platforms, curr)
    return max_platforms
# min_platforms([900,940,950,1100],[910,1200,1120,1130]) → 3

Q: What is the Stock Buy and Sell (one transaction)?
Write the implementation in Python.
A: Track the minimum price so far and maximum profit at each step.
Time: O(n)  |  Space: O(1)
def max_profit(prices):
    min_price = float('inf')
    max_profit = 0
    for price in prices:
        min_price = min(min_price, price)
        max_profit = max(max_profit, price - min_price)
    return max_profit
# max_profit([7,1,5,3,6,4]) → 5 (buy@1, sell@6)

Q: What is the Stock Buy and Sell with Cooldown (DP)?
Write the implementation in Python.
A: After selling, you must wait one day before buying again. Track three states: hold, sold, rest.
Time: O(n)  |  Space: O(1)
def max_profit_cooldown(prices):
    hold = float('-inf')
    sold = 0
    rest = 0
    for price in prices:
        prev_sold = sold
        sold = hold + price
        hold = max(hold, rest - price)
        rest = max(rest, prev_sold)
    return max(sold, rest)
# max_profit_cooldown([1,2,3,0,2]) → 3 (buy@1,sell@3,rest,buy@0,sell@2)

Q: What is the Longest Common Prefix problem?
Write the implementation in Python.
A: Find the longest common prefix among an array of strings. Compare characters column by column.
Time: O(S) where S = sum of all chars  |  Space: O(1)
def longest_common_prefix(strs):
    if not strs:
        return ""
    for i in range(len(strs[0])):
        ch = strs[0][i]
        for s in strs[1:]:
            if i >= len(s) or s[i] != ch:
                return strs[0][:i]
    return strs[0]
# longest_common_prefix(["flower","flow","flight"]) → "fl"

Q: What is the Next Permutation algorithm?
Write the implementation in Python.
A: Find the lexicographically next permutation. 1) Find rightmost i where a[i] < a[i+1]. 2) Swap a[i] with smallest larger element to its right. 3) Reverse the suffix.
Time: O(n)  |  Space: O(1)
def next_permutation(nums):
    n = len(nums)
    i = n - 2
    while i >= 0 and nums[i] >= nums[i + 1]:
        i -= 1
    if i >= 0:
        j = n - 1
        while nums[j] <= nums[i]:
            j -= 1
        nums[i], nums[j] = nums[j], nums[i]
    # Reverse suffix
    lo, hi = i + 1, n - 1
    while lo < hi:
        nums[lo], nums[hi] = nums[hi], nums[lo]
        lo += 1; hi -= 1
# [1,2,3] → [1,3,2] → [2,1,3] → [2,3,1] → [3,1,2] → [3,2,1]

Q: What is the Pow(x, n) — fast exponentiation?
Write the implementation in Python.
A: Compute x^n in O(log n) using binary exponentiation (squaring). Handle negative exponents.
Time: O(log n)  |  Space: O(1)
def my_pow(x, n):
    if n < 0:
        x = 1 / x
        n = -n
    result = 1
    while n:
        if n & 1:
            result *= x
        x *= x
        n >>= 1
    return result
# my_pow(2, 10) → 1024
# my_pow(2, -2) → 0.25

Q: What is the Detect Cycle and Find Start of Cycle in an undirected graph?
Write the DFS implementation in Python.
A: In an undirected graph, a cycle exists if DFS visits a neighbor that is already visited and is not the parent.
Time: O(V + E)  |  Space: O(V)
def has_cycle_undirected(graph, n):
    visited = [False] * n
    def dfs(node, parent):
        visited[node] = True
        for neighbor in graph[node]:
            if not visited[neighbor]:
                if dfs(neighbor, node):
                    return True
            elif neighbor != parent:
                return True
        return False
    return any(not visited[i] and dfs(i, -1) for i in range(n))

Q: What is the Minimum Deletions to Make Character Frequencies Unique (Greedy)?
Write the implementation in Python.
A: Make all character frequencies unique by deleting minimum characters. Greedily reduce duplicate frequencies.
Time: O(n + k²)  |  Space: O(k)
from collections import Counter
def min_deletions(s):
    freq = sorted(Counter(s).values(), reverse=True)
    deletions = 0
    used = set()
    for f in freq:
        while f > 0 and f in used:
            f -= 1
            deletions += 1
        used.add(f)
    return deletions
# min_deletions("aaabbbcc") → 2
# a:3, b:3→2, c:2→1 → delete 2

Q: What is the Reorganize String problem (Greedy + Heap)?
Write the implementation in Python.
A: Rearrange string so no two adjacent characters are same. Greedily place the most frequent character, alternating with others.
Time: O(n log k)  |  Space: O(k)
import heapq
from collections import Counter
def reorganize_string(s):
    count = Counter(s)
    heap = [(-freq, ch) for ch, freq in count.items()]
    heapq.heapify(heap)
    result = []
    prev_freq, prev_ch = 0, ''
    while heap:
        freq, ch = heapq.heappop(heap)
        result.append(ch)
        if prev_freq < 0:
            heapq.heappush(heap, (prev_freq, prev_ch))
        prev_freq, prev_ch = freq + 1, ch
    result = ''.join(result)
    return result if len(result) == len(s) else ""
# reorganize_string("aab") → "aba"

Q: What is the Design a Circular Queue?
Write the implementation in Python.
A: Use a fixed-size array with front and rear pointers that wrap around using modulo arithmetic.
All operations: O(1)  |  Space: O(k)
class MyCircularQueue:
    def __init__(self, k):
        self.q = [0] * k
        self.size = 0
        self.cap = k
        self.front = 0
    def enQueue(self, val):
        if self.isFull(): return False
        idx = (self.front + self.size) % self.cap
        self.q[idx] = val
        self.size += 1
        return True
    def deQueue(self):
        if self.isEmpty(): return False
        self.front = (self.front + 1) % self.cap
        self.size -= 1
        return True
    def Front(self):
        return self.q[self.front] if not self.isEmpty() else -1
    def Rear(self):
        idx = (self.front + self.size - 1) % self.cap
        return self.q[idx] if not self.isEmpty() else -1
    def isEmpty(self): return self.size == 0
    def isFull(self): return self.size == self.cap

Q: What is the Calculate GCD using Euclidean Algorithm?
Write the implementation in Python.
A: GCD(a, b) = GCD(b, a % b) until b == 0. Extended version also finds x, y such that ax + by = gcd(a, b).
Time: O(log(min(a,b)))  |  Space: O(1)
def gcd(a, b):
    while b:
        a, b = b, a % b
    return a
def lcm(a, b):
    return a * b // gcd(a, b)
def extended_gcd(a, b):
    if b == 0:
        return a, 1, 0
    g, x1, y1 = extended_gcd(b, a % b)
    return g, y1, x1 - (a // b) * y1
# gcd(48, 18) → 6
# extended_gcd(48, 18) → (6, -1, 3)  (48*(-1) + 18*3 = 6)

Q: What is Modular Arithmetic for competitive programming?
Write key operations in Python.
A: Operations under modulo for large numbers. Key: (a*b)%m = ((a%m)*(b%m))%m. Use Fermat's little theorem for modular inverse when m is prime.
MOD = 10**9 + 7
# Modular exponentiation
def mod_pow(base, exp, mod=MOD):
    result = 1
    base %= mod
    while exp > 0:
        if exp & 1:
            result = result * base % mod
        exp >>= 1
        base = base * base % mod
    return result
# Modular inverse (when mod is prime)
def mod_inv(a, mod=MOD):
    return mod_pow(a, mod - 2, mod)
# nCr mod p
def ncr(n, r, mod=MOD):
    if r > n: return 0
    num = den = 1
    for i in range(r):
        num = num * (n - i) % mod
        den = den * (i + 1) % mod
    return num * mod_inv(den, mod) % mod

Q: What is the Trie-based XOR Maximum problem?
Write the implementation in Python.
A: Find two numbers in an array with maximum XOR. Insert all numbers into a binary trie (bit by bit from MSB), then greedily pick opposite bits.
Time: O(n * 32)  |  Space: O(n * 32)
class XORTrie:
    def __init__(self):
        self.root = {}
    def insert(self, num):
        node = self.root
        for i in range(31, -1, -1):
            bit = (num >> i) & 1
            if bit not in node:
                node[bit] = {}
            node = node[bit]
    def max_xor(self, num):
        node = self.root
        xor_val = 0
        for i in range(31, -1, -1):
            bit = (num >> i) & 1
            toggle = 1 - bit
            if toggle in node:
                xor_val |= (1 << i)
                node = node[toggle]
            else:
                node = node[bit]
        return xor_val
def find_max_xor(nums):
    trie = XORTrie()
    trie.insert(nums[0])
    best = 0
    for num in nums[1:]:
        best = max(best, trie.max_xor(num))
        trie.insert(num)
    return best

Q: What is the Shortest Path Faster Algorithm (SPFA)?
Write the implementation in Python.
A: SPFA is an optimized Bellman-Ford using a queue. Only relaxes edges from nodes whose distances changed. Faster in practice but worst case O(VE).
Time: O(V * E) worst, much faster avg  |  Space: O(V)
from collections import deque, defaultdict
def spfa(graph, src, n):
    dist = [float('inf')] * n
    dist[src] = 0
    in_queue = [False] * n
    queue = deque([src])
    in_queue[src] = True
    while queue:
        u = queue.popleft()
        in_queue[u] = False
        for v, w in graph[u]:
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                if not in_queue[v]:
                    queue.append(v)
                    in_queue[v] = True
    return dist

Q: What is the 0-1 BFS algorithm?
Write the implementation in Python.
A: Shortest path in graphs with edge weights only 0 or 1. Use a deque: push weight-0 edges to front, weight-1 edges to back.
Time: O(V + E)  |  Space: O(V)
from collections import deque
def bfs_01(graph, src, n):
    dist = [float('inf')] * n
    dist[src] = 0
    dq = deque([src])
    while dq:
        u = dq.popleft()
        for v, w in graph[u]:  # w is 0 or 1
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                if w == 0:
                    dq.appendleft(v)
                else:
                    dq.append(v)
    return dist

Q: What is Mo's Algorithm for offline range queries?
Write the implementation in Python.
A: Answer multiple range queries offline by sorting queries by blocks and moving two pointers. Reduces random access.
Time: O((n + q) * √n)  |  Space: O(n)
import math
def mo_algorithm(arr, queries):
    n = len(arr)
    block = max(1, int(math.sqrt(n)))
    # Sort queries by (l//block, r)
    sorted_q = sorted(enumerate(queries),
        key=lambda x: (x[1][0]//block, x[1][1]))
    ans = [0] * len(queries)
    curr_l, curr_r = 0, -1
    curr_sum = 0
    def add(idx):
        nonlocal curr_sum
        curr_sum += arr[idx]
    def remove(idx):
        nonlocal curr_sum
        curr_sum -= arr[idx]
    for qi, (l, r) in sorted_q:
        while curr_r < r: curr_r += 1; add(curr_r)
        while curr_l > l: curr_l -= 1; add(curr_l)
        while curr_r > r: remove(curr_r); curr_r -= 1
        while curr_l < l: remove(curr_l); curr_l += 1
        ans[qi] = curr_sum
    return ans

Q: What is the Segment Tree with Lazy Propagation?
Write the implementation in Python.
A: Extends Segment Tree to support range updates in O(log n) by deferring updates to children until needed.
Build: O(n)  |  Range Update/Query: O(log n)
class LazySegTree:
    def __init__(self, n):
        self.n = n
        self.tree = [0] * (4 * n)
        self.lazy = [0] * (4 * n)
    def push_down(self, node, l, r):
        if self.lazy[node]:
            mid = (l + r) // 2
            for child, size in [(2*node, mid-l+1), (2*node+1, r-mid)]:
                self.tree[child] += self.lazy[node] * size
                self.lazy[child] += self.lazy[node]
            self.lazy[node] = 0
    def update(self, node, l, r, ql, qr, val):
        if ql <= l and r <= qr:
            self.tree[node] += val * (r - l + 1)
            self.lazy[node] += val
            return
        self.push_down(node, l, r)
        mid = (l + r) // 2
        if ql <= mid: self.update(2*node, l, mid, ql, qr, val)
        if qr > mid: self.update(2*node+1, mid+1, r, ql, qr, val)
        self.tree[node] = self.tree[2*node] + self.tree[2*node+1]
    def query(self, node, l, r, ql, qr):
        if ql <= l and r <= qr:
            return self.tree[node]
        self.push_down(node, l, r)
        mid = (l + r) // 2
        res = 0
        if ql <= mid: res += self.query(2*node, l, mid, ql, qr)
        if qr > mid: res += self.query(2*node+1, mid+1, r, ql, qr)
        return res

Q: What is the Heavy-Light Decomposition (HLD)?
Explain the concept and write the core in Python.
A: HLD decomposes a tree into chains so any path query can be answered with O(log²n) using a segment tree on the chains. Heavy edge = edge to child with largest subtree.
Build: O(n)  |  Path Query: O(log²n)
class HLD:
    def __init__(self, adj, root=0):
        n = len(adj)
        self.parent = [0]*n
        self.depth = [0]*n
        self.heavy = [-1]*n
        self.head = [0]*n
        self.pos = [0]*n
        self.sz = [1]*n
        # DFS to compute subtree sizes and heavy children
        stack = [(root, -1, False)]
        order = []
        while stack:
            u, p, visited = stack.pop()
            if visited:
                for v in adj[u]:
                    if v != p:
                        self.sz[u] += self.sz[v]
                        if self.heavy[u]==-1 or self.sz[v]>self.sz[self.heavy[u]]:
                            self.heavy[u] = v
                continue
            self.parent[u] = p
            stack.append((u, p, True))
            for v in adj[u]:
                if v != p:
                    self.depth[v] = self.depth[u]+1
                    stack.append((v, u, False))
        # Decompose into chains
        cur_pos = 0
        stack = [(root, root)]
        while stack:
            u, h = stack.pop()
            self.head[u] = h
            self.pos[u] = cur_pos
            cur_pos += 1
            # Process light children first (so heavy is processed last = next on stack)
            children = [v for v in adj[u] if v!=self.parent[u] and v!=self.heavy[u]]
            for v in children:
                stack.append((v, v))
            if self.heavy[u] != -1:
                stack.append((self.heavy[u], h))

Q: What is Manacher's Algorithm?
Write the implementation in Python.
A: Manacher's finds the longest palindromic substring in O(n) by exploiting symmetry of palindromes around a center. Uses a transformed string with separators.
Time: O(n)  |  Space: O(n)
def manachers(s):
    t = '#' + '#'.join(s) + '#'
    n = len(t)
    p = [0] * n
    c = r = 0
    for i in range(n):
        mirror = 2 * c - i
        if i < r:
            p[i] = min(r - i, p[mirror])
        while i + p[i] + 1 < n and i - p[i] - 1 >= 0 and t[i + p[i] + 1] == t[i - p[i] - 1]:
            p[i] += 1
        if i + p[i] > r:
            c, r = i, i + p[i]
    max_len = max(p)
    center = p.index(max_len)
    start = (center - max_len) // 2
    return s[start:start + max_len]
# manachers("babad") → "bab"

Q: What is the Binary Tree Maximum Path Sum problem?
Write the implementation in Python.
A: Find the maximum path sum between any two nodes in a binary tree. At each node, decide whether to extend the path or start fresh.
Time: O(n)  |  Space: O(h)
def max_path_sum(root):
    result = [float('-inf')]
    def dfs(node):
        if not node:
            return 0
        left = max(dfs(node.left), 0)
        right = max(dfs(node.right), 0)
        result[0] = max(result[0], node.val + left + right)
        return node.val + max(left, right)
    dfs(root)
    return result[0]

Q: What is the Longest Increasing Path in a Matrix problem?
Write the DFS + memoization solution in Python.
A: Find the longest strictly increasing path in a matrix moving in 4 directions. Use DFS with memoization — no visited set needed since values are strictly increasing.
Time: O(m * n)  |  Space: O(m * n)
def longest_increasing_path(matrix):
    if not matrix: return 0
    m, n = len(matrix), len(matrix[0])
    memo = {}
    def dfs(r, c):
        if (r, c) in memo:
            return memo[(r, c)]
        best = 1
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            nr, nc = r+dr, c+dc
            if 0 <= nr < m and 0 <= nc < n and matrix[nr][nc] > matrix[r][c]:
                best = max(best, 1 + dfs(nr, nc))
        memo[(r, c)] = best
        return best
    return max(dfs(r, c) for r in range(m) for c in range(n))

Q: What is the Remove Nth Node From End of Linked List problem?
Write the implementation in Python.
A: Use two pointers separated by n nodes. When the fast pointer reaches the end, slow is at the node before the target.
Time: O(n)  |  Space: O(1)
def remove_nth_from_end(head, n):
    dummy = ListNode(0, head)
    fast = slow = dummy
    for _ in range(n + 1):
        fast = fast.next
    while fast:
        fast = fast.next
        slow = slow.next
    slow.next = slow.next.next
    return dummy.next

Q: What is the Add Two Numbers (Linked List) problem?
Write the implementation in Python.
A: Add two numbers represented as reversed linked lists. Process digit by digit with a carry.
Time: O(max(m, n))  |  Space: O(max(m, n))
def add_two_numbers(l1, l2):
    dummy = ListNode(0)
    curr = dummy
    carry = 0
    while l1 or l2 or carry:
        val = carry
        if l1:
            val += l1.val
            l1 = l1.next
        if l2:
            val += l2.val
            l2 = l2.next
        carry, digit = divmod(val, 10)
        curr.next = ListNode(digit)
        curr = curr.next
    return dummy.next

Q: What is the Copy List with Random Pointer problem?
Write the implementation in Python.
A: Clone a linked list where each node has a random pointer. Use a hash map to map original→clone nodes.
Time: O(n)  |  Space: O(n)
def copy_random_list(head):
    if not head:
        return None
    old_to_new = {}
    curr = head
    while curr:
        old_to_new[curr] = Node(curr.val)
        curr = curr.next
    curr = head
    while curr:
        old_to_new[curr].next = old_to_new.get(curr.next)
        old_to_new[curr].random = old_to_new.get(curr.random)
        curr = curr.next
    return old_to_new[head]

Q: What is the Sort a Linked List problem?
Write the merge sort solution in Python.
A: Sort a linked list in O(n log n) using merge sort. Find middle with slow/fast pointers, recursively sort halves, then merge.
Time: O(n log n)  |  Space: O(log n)
def sort_list(head):
    if not head or not head.next:
        return head
    slow, fast = head, head.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    mid = slow.next
    slow.next = None
    left = sort_list(head)
    right = sort_list(mid)
    return merge(left, right)
def merge(l1, l2):
    dummy = curr = ListNode(0)
    while l1 and l2:
        if l1.val <= l2.val:
            curr.next = l1
            l1 = l1.next
        else:
            curr.next = l2
            l2 = l2.next
        curr = curr.next
    curr.next = l1 or l2
    return dummy.next

Q: What is the Intersection of Two Linked Lists problem?
Write the implementation in Python.
A: Find the node where two lists merge. Use two pointers — when one reaches the end, redirect to the other list's head. They meet at the intersection.
Time: O(m + n)  |  Space: O(1)
def get_intersection_node(headA, headB):
    a, b = headA, headB
    while a != b:
        a = a.next if a else headB
        b = b.next if b else headA
    return a

Q: What is the Count Inversions problem (merge sort)?
Write the implementation in Python.
A: An inversion is a pair (i, j) where i < j but arr[i] > arr[j]. Count inversions during merge sort — count when right element is picked before left.
Time: O(n log n)  |  Space: O(n)
def count_inversions(arr):
    if len(arr) <= 1:
        return arr, 0
    mid = len(arr) // 2
    left, l_inv = count_inversions(arr[:mid])
    right, r_inv = count_inversions(arr[mid:])
    merged, split_inv = merge_count(left, right)
    return merged, l_inv + r_inv + split_inv
def merge_count(left, right):
    result = []
    inversions = 0
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            inversions += len(left) - i
            j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result, inversions

Q: What is the Russian Doll Envelopes problem (LIS variant)?
Write the implementation in Python.
A: Find max envelopes you can nest. Sort by width ascending, then LIS on heights (with width ties broken by descending height).
Time: O(n log n)  |  Space: O(n)
from bisect import bisect_left
def max_envelopes(envelopes):
    envelopes.sort(key=lambda x: (x[0], -x[1]))
    heights = [e[1] for e in envelopes]
    tails = []
    for h in heights:
        pos = bisect_left(tails, h)
        if pos == len(tails):
            tails.append(h)
        else:
            tails[pos] = h
    return len(tails)

Q: What is the 2D Prefix Sum (Range Sum Query on a Matrix)?
Write the implementation in Python.
A: Precompute cumulative sums for a 2D matrix so any sub-rectangle sum can be answered in O(1).
Build: O(m * n)  |  Query: O(1)
class NumMatrix:
    def __init__(self, matrix):
        m, n = len(matrix), len(matrix[0])
        self.prefix = [[0]*(n+1) for _ in range(m+1)]
        for i in range(m):
            for j in range(n):
                self.prefix[i+1][j+1] = (matrix[i][j]
                    + self.prefix[i][j+1]
                    + self.prefix[i+1][j]
                    - self.prefix[i][j])
    def sum_region(self, r1, c1, r2, c2):
        return (self.prefix[r2+1][c2+1]
              - self.prefix[r1][c2+1]
              - self.prefix[r2+1][c1]
              + self.prefix[r1][c1])

Q: What is the Shortest Common Supersequence problem (DP)?
Write the implementation in Python.
A: Find the shortest string that has both s1 and s2 as subsequences. Build on LCS — include LCS chars once and non-LCS chars from both.
Time: O(m * n)  |  Space: O(m * n)
def shortest_common_supersequence(s1, s2):
    m, n = len(s1), len(s2)
    dp = [[""]*(n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        dp[i][0] = s1[:i]
    for j in range(1, n+1):
        dp[0][j] = s2[:j]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1] + s1[i-1]
            else:
                a, b = dp[i-1][j], dp[i][j-1]
                dp[i][j] = (a + s1[i-1]) if len(a) <= len(b) else (b + s2[j-1])
    return dp[m][n]

Q: What is the LCA with Binary Lifting?
Write the implementation in Python.
A: Precompute 2^k-th ancestors for all nodes. To find LCA, lift both nodes to the same depth, then lift together until they meet.
Build: O(n log n)  |  Query: O(log n)
import math
class BinaryLifting:
    def __init__(self, adj, root=0):
        n = len(adj)
        self.LOG = max(1, int(math.log2(n)) + 1)
        self.depth = [0] * n
        self.up = [[0]*n for _ in range(self.LOG)]
        # BFS to set depths and parents
        from collections import deque
        visited = [False] * n
        visited[root] = True
        queue = deque([root])
        while queue:
            u = queue.popleft()
            for v in adj[u]:
                if not visited[v]:
                    visited[v] = True
                    self.depth[v] = self.depth[u] + 1
                    self.up[0][v] = u
                    queue.append(v)
        for k in range(1, self.LOG):
            for v in range(n):
                self.up[k][v] = self.up[k-1][self.up[k-1][v]]
    def lca(self, u, v):
        if self.depth[u] < self.depth[v]:
            u, v = v, u
        diff = self.depth[u] - self.depth[v]
        for k in range(self.LOG):
            if (diff >> k) & 1:
                u = self.up[k][u]
        if u == v:
            return u
        for k in range(self.LOG-1, -1, -1):
            if self.up[k][u] != self.up[k][v]:
                u = self.up[k][u]
                v = self.up[k][v]
        return self.up[0][u]

Q: What is Kosaraju's Algorithm for Strongly Connected Components?
Write the implementation in Python.
A: Kosaraju's finds all SCCs using two DFS passes: first on the original graph (record finish order), then on the reversed graph in reverse finish order.
Time: O(V + E)  |  Space: O(V)
from collections import defaultdict
def kosaraju(graph, n):
    visited = [False] * n
    order = []
    def dfs1(u):
        visited[u] = True
        for v in graph[u]:
            if not visited[v]:
                dfs1(v)
        order.append(u)
    for i in range(n):
        if not visited[i]:
            dfs1(i)
    # Build reverse graph
    rev = defaultdict(list)
    for u in graph:
        for v in graph[u]:
            rev[v].append(u)
    visited = [False] * n
    sccs = []
    def dfs2(u, comp):
        visited[u] = True
        comp.append(u)
        for v in rev[u]:
            if not visited[v]:
                dfs2(v, comp)
    for u in reversed(order):
        if not visited[u]:
            comp = []
            dfs2(u, comp)
            sccs.append(comp)
    return sccs

Q: What is Dinic's Algorithm for Maximum Flow?
Write the implementation in Python.
A: Dinic's finds max flow using BFS to build level graph + DFS to find blocking flows. Faster than Edmonds-Karp for most cases.
Time: O(V² * E)  |  Space: O(V + E)
from collections import deque
class Dinic:
    def __init__(self, n):
        self.n = n
        self.graph = [[] for _ in range(n)]
    def add_edge(self, u, v, cap):
        self.graph[u].append([v, cap, len(self.graph[v])])
        self.graph[v].append([u, 0, len(self.graph[u])-1])
    def bfs(self, s, t):
        self.level = [-1] * self.n
        self.level[s] = 0
        q = deque([s])
        while q:
            u = q.popleft()
            for v, cap, _ in self.graph[u]:
                if cap > 0 and self.level[v] == -1:
                    self.level[v] = self.level[u] + 1
                    q.append(v)
        return self.level[t] != -1
    def dfs(self, u, t, f):
        if u == t: return f
        while self.iter[u] < len(self.graph[u]):
            v, cap, rev = self.graph[u][self.iter[u]]
            if cap > 0 and self.level[v] == self.level[u] + 1:
                d = self.dfs(v, t, min(f, cap))
                if d > 0:
                    self.graph[u][self.iter[u]][1] -= d
                    self.graph[v][rev][1] += d
                    return d
            self.iter[u] += 1
        return 0
    def max_flow(self, s, t):
        flow = 0
        while self.bfs(s, t):
            self.iter = [0] * self.n
            while True:
                f = self.dfs(s, t, float('inf'))
                if f == 0: break
                flow += f
        return flow

Q: What is the Minimum Height Trees problem?
Write the implementation in Python.
A: Find roots that produce trees with minimum height. Iteratively remove leaf nodes (degree 1) until 1 or 2 nodes remain — those are the centers.
Time: O(V)  |  Space: O(V)
from collections import deque
def find_min_height_trees(n, edges):
    if n <= 2:
        return list(range(n))
    adj = [set() for _ in range(n)]
    for u, v in edges:
        adj[u].add(v)
        adj[v].add(u)
    leaves = deque(i for i in range(n) if len(adj[i]) == 1)
    remaining = n
    while remaining > 2:
        new_leaves = []
        remaining -= len(leaves)
        for leaf in leaves:
            neighbor = adj[leaf].pop()
            adj[neighbor].remove(leaf)
            if len(adj[neighbor]) == 1:
                new_leaves.append(neighbor)
        leaves = deque(new_leaves)
    return list(leaves)

Q: What is the Remove Duplicate Letters (Smallest Subsequence) problem?
Write the monotonic stack solution in Python.
A: Remove duplicate letters so each appears once and result is the lexicographically smallest. Use a monotonic stack with last-occurrence tracking.
Time: O(n)  |  Space: O(1)
def remove_duplicate_letters(s):
    last = {ch: i for i, ch in enumerate(s)}
    stack = []
    in_stack = set()
    for i, ch in enumerate(s):
        if ch in in_stack:
            continue
        while stack and ch < stack[-1] and last[stack[-1]] > i:
            in_stack.remove(stack.pop())
        stack.append(ch)
        in_stack.add(ch)
    return ''.join(stack)
# remove_duplicate_letters("bcabc") → "abc"
# remove_duplicate_letters("cbacdcbc") → "acdb"

Q: What is the Random Pick with Weight problem?
Write the implementation in Python.
A: Select an index with probability proportional to its weight. Build prefix sums and use binary search on a random value.
Init: O(n)  |  Pick: O(log n)
import random
from bisect import bisect_left
class RandomPickWeight:
    def __init__(self, w):
        self.prefix = []
        total = 0
        for weight in w:
            total += weight
            self.prefix.append(total)
        self.total = total
    def pick_index(self):
        target = random.random() * self.total
        return bisect_left(self.prefix, target)
# w = [1, 3] → index 0 with prob 0.25, index 1 with prob 0.75

Q: What is the Sqrt Decomposition technique?
Write the implementation in Python.
A: Divide array into blocks of size √n. Precompute answers per block. Queries span at most 2 partial blocks + full blocks in between.
Build: O(n)  |  Query/Update: O(√n)
import math
class SqrtDecomp:
    def __init__(self, arr):
        self.arr = arr[:]
        self.n = len(arr)
        self.block = max(1, int(math.sqrt(self.n)))
        self.blocks = [0] * ((self.n // self.block) + 1)
        for i in range(self.n):
            self.blocks[i // self.block] += arr[i]
    def update(self, i, val):
        self.blocks[i // self.block] += val - self.arr[i]
        self.arr[i] = val
    def query(self, l, r):
        total = 0
        while l <= r:
            if l % self.block == 0 and l + self.block - 1 <= r:
                total += self.blocks[l // self.block]
                l += self.block
            else:
                total += self.arr[l]
                l += 1
        return total

Q: What is the Largest BST Subtree problem?
Write the implementation in Python.
A: Find the size of the largest BST contained in a binary tree. Use post-order DFS returning (is_bst, size, min_val, max_val).
Time: O(n)  |  Space: O(h)
def largest_bst_subtree(root):
    result = [0]
    def dfs(node):
        if not node:
            return (True, 0, float('inf'), float('-inf'))
        l_bst, l_size, l_min, l_max = dfs(node.left)
        r_bst, r_size, r_min, r_max = dfs(node.right)
        if l_bst and r_bst and l_max < node.val < r_min:
            size = l_size + r_size + 1
            result[0] = max(result[0], size)
            return (True, size, min(l_min, node.val), max(r_max, node.val))
        return (False, 0, 0, 0)
    dfs(root)
    return result[0]

Q: What is the Path Sum III problem?
Write the prefix sum solution in Python.
A: Count paths in a binary tree that sum to target. Paths go downward only. Use prefix sum + hash map (like subarray sum equals k).
Time: O(n)  |  Space: O(n)
from collections import defaultdict
def path_sum_3(root, target):
    prefix_count = defaultdict(int)
    prefix_count[0] = 1
    result = [0]
    def dfs(node, curr_sum):
        if not node:
            return
        curr_sum += node.val
        result[0] += prefix_count[curr_sum - target]
        prefix_count[curr_sum] += 1
        dfs(node.left, curr_sum)
        dfs(node.right, curr_sum)
        prefix_count[curr_sum] -= 1
    dfs(root, 0)
    return result[0]

Q: What is the All Paths From Source to Target problem (DAG)?
Write the implementation in Python.
A: Find all paths from node 0 to node n-1 in a DAG. Use DFS/backtracking — no visited set needed since it's a DAG.
Time: O(2^n * n)  |  Space: O(n)
def all_paths(graph):
    target = len(graph) - 1
    result = []
    def dfs(node, path):
        if node == target:
            result.append(path[:])
            return
        for neighbor in graph[node]:
            path.append(neighbor)
            dfs(neighbor, path)
            path.pop()
    dfs(0, [0])
    return result
# graph = [[1,2],[3],[3],[]]
# all_paths(graph) → [[0,1,3],[0,2,3]]

Q: What is the Longest Path in a DAG?
Write the implementation in Python.
A: Use topological sort + DP. Process nodes in topological order, relaxing edges to find the longest distance.
Time: O(V + E)  |  Space: O(V)
from collections import deque
def longest_path_dag(graph, n):
    in_degree = [0] * n
    for u in range(n):
        for v, w in graph[u]:
            in_degree[v] += 1
    dist = [0] * n
    queue = deque(i for i in range(n) if in_degree[i] == 0)
    while queue:
        u = queue.popleft()
        for v, w in graph[u]:
            dist[v] = max(dist[v], dist[u] + w)
            in_degree[v] -= 1
            if in_degree[v] == 0:
                queue.append(v)
    return max(dist)

Q: What is the Flatten Binary Tree to Linked List problem?
Write the implementation in Python.
A: Flatten a binary tree to a linked list in-place following preorder. Use Morris-style: link left subtree's rightmost node to right subtree, move left to right.
Time: O(n)  |  Space: O(1)
def flatten(root):
    curr = root
    while curr:
        if curr.left:
            rightmost = curr.left
            while rightmost.right:
                rightmost = rightmost.right
            rightmost.right = curr.right
            curr.right = curr.left
            curr.left = None
        curr = curr.right

Q: What is the Minimum Operations to Reduce X to Zero problem?
Write the sliding window solution in Python.
A: Remove elements from left/right ends to reduce x to 0 with minimum operations. Equivalent to finding the longest subarray with sum = total - x.
Time: O(n)  |  Space: O(1)
def min_operations(nums, x):
    target = sum(nums) - x
    if target < 0: return -1
    if target == 0: return len(nums)
    left = 0
    curr_sum = 0
    max_len = -1
    for right in range(len(nums)):
        curr_sum += nums[right]
        while curr_sum > target:
            curr_sum -= nums[left]
            left += 1
        if curr_sum == target:
            max_len = max(max_len, right - left + 1)
    return len(nums) - max_len if max_len != -1 else -1

Q: What is the Detect Squares (count axis-aligned squares) problem?
Write the implementation in Python.
A: Maintain a set of points. For a query point, find all points at the same y-coordinate, compute the side length, and check if the other two corners exist.
Add: O(1)  |  Count: O(n)
from collections import defaultdict
class DetectSquares:
    def __init__(self):
        self.pts = defaultdict(int)
    def add(self, point):
        self.pts[tuple(point)] += 1
    def count(self, point):
        px, py = point
        total = 0
        for (x, y), cnt in self.pts.items():
            if y != py or x == px:
                continue
            side = abs(x - px)
            for dy in [side, -side]:
                c1 = self.pts.get((px, py+dy), 0)
                c2 = self.pts.get((x, py+dy), 0)
                total += cnt * c1 * c2
        return total

Q: What is the Euler Tour of a Tree?
Write the implementation in Python.
A: Euler Tour linearizes a tree into an array by recording each node when entering and leaving. Used for subtree queries and LCA with RMQ.
Time: O(n)  |  Space: O(n)
def euler_tour(adj, root=0):
    n = len(adj)
    tin = [0] * n
    tout = [0] * n
    order = []
    timer = [0]
    def dfs(u, parent):
        tin[u] = timer[0]
        order.append(u)
        timer[0] += 1
        for v in adj[u]:
            if v != parent:
                dfs(v, u)
        tout[u] = timer[0] - 1
    dfs(root, -1)
    return tin, tout, order
# Subtree of u = order[tin[u]:tout[u]+1]
# u is ancestor of v iff tin[u] <= tin[v] and tout[v] <= tout[u]

Q: What is the 2-SAT problem?
Write the implementation in Python.
A: Determine if a boolean formula in 2-CNF form is satisfiable. Build an implication graph and find SCCs. Unsatisfiable if x and ¬x are in the same SCC.
Time: O(V + E)  |  Space: O(V)
def solve_2sat(n, clauses):
    # n variables (0..n-1), neg of x = x+n
    graph = [[] for _ in range(2*n)]
    rgraph = [[] for _ in range(2*n)]
    def neg(x): return x+n if x < n else x-n
    for a, b in clauses:  # (a OR b)
        graph[neg(a)].append(b)
        graph[neg(b)].append(a)
        rgraph[b].append(neg(a))
        rgraph[a].append(neg(b))
    # Kosaraju's SCC
    visited = [False]*(2*n)
    order = []
    def dfs1(u):
        visited[u] = True
        for v in graph[u]:
            if not visited[v]: dfs1(v)
        order.append(u)
    for i in range(2*n):
        if not visited[i]: dfs1(i)
    comp = [-1]*(2*n)
    c = 0
    def dfs2(u, c):
        comp[u] = c
        for v in rgraph[u]:
            if comp[v] == -1: dfs2(v, c)
    for u in reversed(order):
        if comp[u] == -1:
            dfs2(u, c)
            c += 1
    for i in range(n):
        if comp[i] == comp[i+n]: return None
    return [comp[i] > comp[i+n] for i in range(n)]

Q: What is the Aho-Corasick multi-pattern string matching algorithm?
Write the implementation in Python.
A: Aho-Corasick searches for multiple patterns simultaneously in a text. Builds a trie + failure links (like KMP's failure function on a trie).
Time: O(n + m + z) where z = matches  |  Space: O(m * Σ)
from collections import deque, defaultdict
class AhoCorasick:
    def __init__(self):
        self.goto = [{}]
        self.fail = [0]
        self.output = [[]]
    def add_pattern(self, pattern, idx):
        state = 0
        for ch in pattern:
            if ch not in self.goto[state]:
                self.goto.append({})
                self.fail.append(0)
                self.output.append([])
                self.goto[state][ch] = len(self.goto) - 1
            state = self.goto[state][ch]
        self.output[state].append(idx)
    def build(self):
        queue = deque()
        for ch, s in self.goto[0].items():
            queue.append(s)
        while queue:
            u = queue.popleft()
            for ch, v in self.goto[u].items():
                queue.append(v)
                f = self.fail[u]
                while f and ch not in self.goto[f]:
                    f = self.fail[f]
                self.fail[v] = self.goto[f].get(ch, 0)
                if self.fail[v] == v: self.fail[v] = 0
                self.output[v] = self.output[v] + self.output[self.fail[v]]
    def search(self, text):
        state = 0
        results = []
        for i, ch in enumerate(text):
            while state and ch not in self.goto[state]:
                state = self.fail[state]
            state = self.goto[state].get(ch, 0)
            for pid in self.output[state]:
                results.append((i, pid))
        return results

Q: What is the Centroid Decomposition of a tree?
Write the implementation in Python.
A: Centroid Decomposition recursively finds the centroid (node whose removal splits the tree into components of size ≤ n/2) and builds a decomposition tree. Used for path queries on trees.
Build: O(n log n)  |  Space: O(n)
def centroid_decomposition(adj, n):
    sz = [0] * n
    removed = [False] * n
    cd_parent = [-1] * n
    def get_size(u, p):
        sz[u] = 1
        for v in adj[u]:
            if v != p and not removed[v]:
                get_size(v, u)
                sz[u] += sz[v]
        return sz[u]
    def get_centroid(u, p, tree_size):
        for v in adj[u]:
            if v != p and not removed[v] and sz[v] > tree_size // 2:
                return get_centroid(v, u, tree_size)
        return u
    def build(u, p):
        tree_size = get_size(u, -1)
        c = get_centroid(u, -1, tree_size)
        removed[c] = True
        cd_parent[c] = p
        for v in adj[c]:
            if not removed[v]:
                build(v, c)
    build(0, -1)
    return cd_parent

Q: What is the Suffix Array and how do you build it?
Write the O(n log²n) implementation in Python.
A: A Suffix Array is a sorted array of all suffixes of a string. Combined with the LCP array, it solves many string problems efficiently.
Build: O(n log²n)  |  Space: O(n)
def build_suffix_array(s):
    n = len(s)
    sa = list(range(n))
    rank = [ord(c) for c in s]
    tmp = [0] * n
    k = 1
    while k < n:
        def cmp_key(i):
            return (rank[i], rank[i + k] if i + k < n else -1)
        sa.sort(key=cmp_key)
        tmp[sa[0]] = 0
        for i in range(1, n):
            tmp[sa[i]] = tmp[sa[i-1]]
            if cmp_key(sa[i]) != cmp_key(sa[i-1]):
                tmp[sa[i]] += 1
        rank = tmp[:]
        k *= 2
    return sa
def build_lcp(s, sa):
    n = len(s)
    rank = [0] * n
    for i in range(n):
        rank[sa[i]] = i
    lcp = [0] * n
    k = 0
    for i in range(n):
        if rank[i] == 0:
            k = 0
            continue
        j = sa[rank[i] - 1]
        while i+k < n and j+k < n and s[i+k] == s[j+k]:
            k += 1
        lcp[rank[i]] = k
        if k: k -= 1
    return lcp

Q: What is the Persistent Segment Tree?
Explain the concept and write the core in Python.
A: A Persistent Segment Tree keeps all previous versions after updates by creating new nodes on the update path instead of modifying existing ones. Only O(log n) new nodes per update.
Update: O(log n)  |  Space: O(n + q log n)
class Node:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
def build(arr, lo, hi):
    if lo == hi:
        return Node(arr[lo])
    mid = (lo + hi) // 2
    return Node(
        left=build(arr, lo, mid),
        right=build(arr, mid+1, hi),
        val=build(arr, lo, mid).val + build(arr, mid+1, hi).val)
def update(node, lo, hi, idx, val):
    if lo == hi:
        return Node(val)
    mid = (lo + hi) // 2
    if idx <= mid:
        new_left = update(node.left, lo, mid, idx, val)
        return Node(new_left.val + node.right.val, new_left, node.right)
    else:
        new_right = update(node.right, mid+1, hi, idx, val)
        return Node(node.left.val + new_right.val, node.left, new_right)
def query(node, lo, hi, ql, qr):
    if ql <= lo and hi <= qr:
        return node.val
    if hi < ql or lo > qr:
        return 0
    mid = (lo + hi) // 2
    return query(node.left, lo, mid, ql, qr) + query(node.right, mid+1, hi, ql, qr)
# roots = []; roots.append(build(arr, 0, n-1))
# roots.append(update(roots[-1], 0, n-1, idx, val))  # new version

Q: What is the Treap (Tree + Heap) data structure?
Write the implementation in Python.
A: A Treap is a randomized BST where each node has a random priority. BST property on keys, heap property on priorities. Expected O(log n) operations.
Insert/Delete/Search: O(log n) expected
import random
class TreapNode:
    def __init__(self, key):
        self.key = key
        self.pri = random.random()
        self.left = self.right = None
        self.size = 1
def size(t): return t.size if t else 0
def update(t):
    if t: t.size = 1 + size(t.left) + size(t.right)
def split(t, key):
    if not t: return None, None
    if t.key <= key:
        t.right, right = split(t.right, key)
        update(t)
        return t, right
    else:
        left, t.left = split(t.left, key)
        update(t)
        return left, t
def merge(left, right):
    if not left or not right:
        return left or right
    if left.pri > right.pri:
        left.right = merge(left.right, right)
        update(left)
        return left
    else:
        right.left = merge(left, right.left)
        update(right)
        return right
def insert(root, key):
    left, right = split(root, key)
    return merge(merge(left, TreapNode(key)), right)
def erase(root, key):
    left, right = split(root, key)
    left2, _ = split(left, key - 1)
    return merge(left2, right)

Q: What is the Skip List data structure?
Write the implementation in Python.
A: A Skip List is a probabilistic alternative to balanced BSTs. Multiple linked list layers where higher layers skip more elements, giving O(log n) search on average.
Search/Insert/Delete: O(log n) expected  |  Space: O(n)
import random
class SkipNode:
    def __init__(self, val, level):
        self.val = val
        self.next = [None] * (level + 1)
class SkipList:
    def __init__(self, max_level=16):
        self.max_level = max_level
        self.head = SkipNode(float('-inf'), max_level)
        self.level = 0
    def _random_level(self):
        lvl = 0
        while random.random() < 0.5 and lvl < self.max_level:
            lvl += 1
        return lvl
    def search(self, target):
        curr = self.head
        for i in range(self.level, -1, -1):
            while curr.next[i] and curr.next[i].val < target:
                curr = curr.next[i]
        curr = curr.next[0]
        return curr and curr.val == target
    def insert(self, val):
        update = [self.head] * (self.max_level + 1)
        curr = self.head
        for i in range(self.level, -1, -1):
            while curr.next[i] and curr.next[i].val < val:
                curr = curr.next[i]
            update[i] = curr
        lvl = self._random_level()
        self.level = max(self.level, lvl)
        node = SkipNode(val, lvl)
        for i in range(lvl + 1):
            node.next[i] = update[i].next[i]
            update[i].next[i] = node

Q: What is the Bloom Filter data structure?
Write the implementation in Python.
A: A Bloom Filter is a space-efficient probabilistic set that can have false positives but no false negatives. Uses multiple hash functions mapping to a bit array.
Add/Check: O(k)  |  Space: O(m) bits
import hashlib
class BloomFilter:
    def __init__(self, size=1000, num_hashes=3):
        self.size = size
        self.num_hashes = num_hashes
        self.bits = [False] * size
    def _hashes(self, item):
        for i in range(self.num_hashes):
            h = hashlib.md5(f"{item}{i}".encode()).hexdigest()
            yield int(h, 16) % self.size
    def add(self, item):
        for idx in self._hashes(item):
            self.bits[idx] = True
    def might_contain(self, item):
        return all(self.bits[idx] for idx in self._hashes(item))
# bf = BloomFilter(10000, 5)
# bf.add("hello")
# bf.might_contain("hello") → True
# bf.might_contain("world") → probably False

Q: What is the Palindrome Pairs problem?
Write the implementation in Python.
A: Find all pairs (i, j) where words[i] + words[j] is a palindrome. Use a hash map of reversed words and check splits.
Time: O(n * k²) where k = avg word length  |  Space: O(n * k)
def palindrome_pairs(words):
    word_map = {w: i for i, w in enumerate(words)}
    result = []
    for i, word in enumerate(words):
        for j in range(len(word) + 1):
            prefix, suffix = word[:j], word[j:]
            # If suffix is palindrome, check if reversed prefix exists
            if suffix == suffix[::-1]:
                rev_pre = prefix[::-1]
                if rev_pre in word_map and word_map[rev_pre] != i:
                    result.append([i, word_map[rev_pre]])
            # If prefix is palindrome, check if reversed suffix exists
            if j > 0 and prefix == prefix[::-1]:
                rev_suf = suffix[::-1]
                if rev_suf in word_map and word_map[rev_suf] != i:
                    result.append([word_map[rev_suf], i])
    return result

Q: What is the Trapping Rain Water II (3D) problem?
Write the min-heap solution in Python.
A: 3D version: find water trapped in a 2D height map. Use a min-heap of boundary cells, expanding inward and tracking the max boundary height seen.
Time: O(m * n * log(m*n))  |  Space: O(m * n)
import heapq
def trap_rain_water_3d(heightMap):
    if not heightMap: return 0
    m, n = len(heightMap), len(heightMap[0])
    visited = [[False]*n for _ in range(m)]
    heap = []
    # Add all boundary cells
    for r in range(m):
        for c in range(n):
            if r == 0 or r == m-1 or c == 0 or c == n-1:
                heapq.heappush(heap, (heightMap[r][c], r, c))
                visited[r][c] = True
    water = 0
    while heap:
        h, r, c = heapq.heappop(heap)
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            nr, nc = r+dr, c+dc
            if 0<=nr                visited[nr][nc] = True
                water += max(0, h - heightMap[nr][nc])
                heapq.heappush(heap, (max(h, heightMap[nr][nc]), nr, nc))
    return water

Q: What is the Minimum Window Subsequence problem?
Write the DP solution in Python.
A: Find the smallest window in s that contains t as a subsequence (characters in order). Use DP or two-pointer approach.
Time: O(n * m)  |  Space: O(n)
def min_window_subsequence(s, t):
    n, m = len(s), len(t)
    best = ""
    best_len = float('inf')
    i = 0
    while i < n:
        # Forward: find end of window
        j = 0
        k = i
        while k < n and j < m:
            if s[k] == t[j]:
                j += 1
            k += 1
        if j < m: break
        end = k - 1
        # Backward: shrink window from end
        j = m - 1
        while j >= 0:
            if s[end] == t[j]:
                j -= 1
            end -= 1
        start = end + 1
        if k - start < best_len:
            best_len = k - start
            best = s[start:start + best_len]
        i = start + 1
    return best

Q: What is the Maximum Frequency Stack problem?
Write the implementation in Python.
A: Design a stack that pops the most frequent element. On tie, pop the most recently pushed. Use a freq map + stack per frequency.
Push/Pop: O(1)  |  Space: O(n)
from collections import defaultdict
class FreqStack:
    def __init__(self):
        self.freq = defaultdict(int)
        self.group = defaultdict(list)
        self.max_freq = 0
    def push(self, val):
        self.freq[val] += 1
        f = self.freq[val]
        self.max_freq = max(self.max_freq, f)
        self.group[f].append(val)
    def pop(self):
        val = self.group[self.max_freq].pop()
        self.freq[val] -= 1
        if not self.group[self.max_freq]:
            self.max_freq -= 1
        return val

Q: What is the Minimum Cost to Cut a Stick problem (interval DP)?
Write the implementation in Python.
A: Cut a stick at given positions minimizing total cost (cost = stick length at each cut). Classic interval DP — try each cut as the last one in a range.
Time: O(n³)  |  Space: O(n²)
def min_cost(n, cuts):
    cuts = sorted([0] + cuts + [n])
    m = len(cuts)
    dp = [[0]*m for _ in range(m)]
    for length in range(2, m):
        for i in range(m - length):
            j = i + length
            dp[i][j] = float('inf')
            for k in range(i+1, j):
                dp[i][j] = min(dp[i][j],
                    dp[i][k] + dp[k][j] + cuts[j] - cuts[i])
    return dp[0][m-1]

Q: What is the Kth Largest Element in a Stream problem?
Write the implementation in Python.
A: Maintain a min-heap of size k. The root is always the k-th largest element.
Add: O(log k)  |  Space: O(k)
import heapq
class KthLargest:
    def __init__(self, k, nums):
        self.k = k
        self.heap = nums
        heapq.heapify(self.heap)
        while len(self.heap) > k:
            heapq.heappop(self.heap)
    def add(self, val):
        heapq.heappush(self.heap, val)
        if len(self.heap) > self.k:
            heapq.heappop(self.heap)
        return self.heap[0]

Q: What is the Minimum Number of Refueling Stops problem (Greedy + Heap)?
Write the implementation in Python.
A: Drive as far as possible, and when stuck, retroactively refuel at the best past station using a max-heap of fuel amounts.
Time: O(n log n)  |  Space: O(n)
import heapq
def min_refuel_stops(target, start_fuel, stations):
    heap = []  # max-heap (negate fuel)
    fuel = start_fuel
    stops = 0
    prev = 0
    for pos, gas in stations + [(target, 0)]:
        fuel -= (pos - prev)
        while heap and fuel < 0:
            fuel += -heapq.heappop(heap)
            stops += 1
        if fuel < 0:
            return -1
        heapq.heappush(heap, -gas)
        prev = pos
    return stops

Q: What is the Shortest Bridge problem (DFS + BFS)?
Write the implementation in Python.
A: Find the shortest distance between two islands. DFS to find one island, then BFS to expand until reaching the other.
Time: O(m * n)  |  Space: O(m * n)
from collections import deque
def shortest_bridge(grid):
    m, n = len(grid), len(grid[0])
    queue = deque()
    found = False
    def dfs(r, c):
        if r<0 or r>=m or c<0 or c>=n or grid[r][c] != 1:
            return
        grid[r][c] = 2
        queue.append((r, c, 0))
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            dfs(r+dr, c+dc)
    # Find and mark first island
    for r in range(m):
        for c in range(n):
            if grid[r][c] == 1 and not found:
                dfs(r, c)
                found = True
    # BFS from first island to second
    while queue:
        r, c, dist = queue.popleft()
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            nr, nc = r+dr, c+dc
            if 0<=nr                if grid[nr][nc] == 1:
                    return dist
                if grid[nr][nc] == 0:
                    grid[nr][nc] = 2
                    queue.append((nr, nc, dist+1))
    return -1

Q: What is the Coin Change II (number of ways) problem?
Write the implementation in Python.
A: Count the number of combinations that make up the amount (unlike Coin Change I which finds minimum coins). Unbounded knapsack variant.
Time: O(n * amount)  |  Space: O(amount)
def change(amount, coins):
    dp = [0] * (amount + 1)
    dp[0] = 1
    for coin in coins:
        for j in range(coin, amount + 1):
            dp[j] += dp[j - coin]
    return dp[amount]
# change(5, [1,2,5]) → 4
# Ways: [5], [2+2+1], [2+1+1+1], [1+1+1+1+1]

Q: What is the Minimum Number of Arrows to Burst Balloons (Greedy)?
Write the implementation in Python.
A: Find minimum arrows to burst all balloons (intervals). Sort by end point, shoot at the end of the earliest-ending balloon, skip all overlapping ones.
Time: O(n log n)  |  Space: O(1)
def find_min_arrows(points):
    if not points: return 0
    points.sort(key=lambda x: x[1])
    arrows = 1
    end = points[0][1]
    for s, e in points[1:]:
        if s > end:
            arrows += 1
            end = e
    return arrows
# find_min_arrows([[10,16],[2,8],[1,6],[7,12]]) → 2

Q: What is the Non-overlapping Intervals problem (Greedy)?
Write the implementation in Python.
A: Find the minimum number of intervals to remove to make the rest non-overlapping. Equivalent to: total - max non-overlapping (activity selection).
Time: O(n log n)  |  Space: O(1)
def erase_overlap_intervals(intervals):
    intervals.sort(key=lambda x: x[1])
    count = 0
    end = float('-inf')
    for s, e in intervals:
        if s >= end:
            end = e
        else:
            count += 1
    return count
# erase_overlap_intervals([[1,2],[2,3],[3,4],[1,3]]) → 1

Q: What is the Insert Interval problem?
Write the implementation in Python.
A: Insert a new interval into a sorted list of non-overlapping intervals, merging if necessary.
Time: O(n)  |  Space: O(n)
def insert_interval(intervals, new):
    result = []
    i = 0
    n = len(intervals)
    # Add all intervals ending before new starts
    while i < n and intervals[i][1] < new[0]:
        result.append(intervals[i])
        i += 1
    # Merge overlapping intervals with new
    while i < n and intervals[i][0] <= new[1]:
        new[0] = min(new[0], intervals[i][0])
        new[1] = max(new[1], intervals[i][1])
        i += 1
    result.append(new)
    # Add remaining
    while i < n:
        result.append(intervals[i])
        i += 1
    return result

Q: What is the Car Fleet problem?
Write the implementation in Python.
A: Cars at different positions/speeds drive toward a target. A slower car ahead blocks faster cars behind it, forming a fleet. Count the number of fleets.
Time: O(n log n)  |  Space: O(n)
def car_fleet(target, position, speed):
    cars = sorted(zip(position, speed), reverse=True)
    fleets = 0
    prev_time = 0
    for pos, spd in cars:
        time = (target - pos) / spd
        if time > prev_time:
            fleets += 1
            prev_time = time
    return fleets
# car_fleet(12, [10,8,0,5,3], [2,4,1,1,3]) → 3

Q: What is the Design an LFU (Least Frequently Used) Cache?
Write the implementation in Python.
A: Evicts the least frequently used item (tie-break by least recently used). Use a freq→OrderedDict mapping + key→freq map.
Get/Put: O(1)  |  Space: O(capacity)
from collections import defaultdict, OrderedDict
class LFUCache:
    def __init__(self, capacity):
        self.cap = capacity
        self.min_freq = 0
        self.key_val = {}
        self.key_freq = {}
        self.freq_keys = defaultdict(OrderedDict)
    def _update(self, key):
        freq = self.key_freq[key]
        del self.freq_keys[freq][key]
        if not self.freq_keys[freq] and freq == self.min_freq:
            self.min_freq += 1
        self.key_freq[key] = freq + 1
        self.freq_keys[freq + 1][key] = None
    def get(self, key):
        if key not in self.key_val:
            return -1
        self._update(key)
        return self.key_val[key]
    def put(self, key, value):
        if self.cap <= 0: return
        if key in self.key_val:
            self.key_val[key] = value
            self._update(key)
            return
        if len(self.key_val) >= self.cap:
            evict, _ = self.freq_keys[self.min_freq].popitem(last=False)
            del self.key_val[evict]
            del self.key_freq[evict]
        self.key_val[key] = value
        self.key_freq[key] = 1
        self.freq_keys[1][key] = None
        self.min_freq = 1

Q: What is the Maximum Sum of Rectangle No Larger Than K?
Write the implementation in Python.
A: Find the rectangle in a matrix with the max sum ≤ k. Fix left/right columns, use prefix sums on rows + sorted set (bisect) to find best subarray sum ≤ k.
Time: O(n² * m * log m)  |  Space: O(m)
from bisect import bisect_left, insort
def max_sum_submatrix(matrix, k):
    m, n = len(matrix), len(matrix[0])
    result = float('-inf')
    for left in range(n):
        row_sum = [0] * m
        for right in range(left, n):
            for r in range(m):
                row_sum[r] += matrix[r][right]
            # Find max subarray sum <= k
            sorted_sums = [0]
            prefix = 0
            for s in row_sum:
                prefix += s
                idx = bisect_left(sorted_sums, prefix - k)
                if idx < len(sorted_sums):
                    result = max(result, prefix - sorted_sums[idx])
                insort(sorted_sums, prefix)
    return result

Q: What is the Stone Game problem (DP / Game Theory)?
Write the implementation in Python.
A: Two players take stones from either end of a row. Determine if player 1 wins with optimal play. dp[i][j] = max score difference the current player can achieve from piles[i..j].
Time: O(n²)  |  Space: O(n²)
def stone_game(piles):
    n = len(piles)
    dp = [[0]*n for _ in range(n)]
    for i in range(n):
        dp[i][i] = piles[i]
    for length in range(2, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            dp[i][j] = max(
                piles[i] - dp[i+1][j],
                piles[j] - dp[i][j-1])
    return dp[0][n-1] > 0
# dp[0][n-1] > 0 means player 1 wins

Q: What is the Count of Range Sum problem (merge sort)?
Write the implementation in Python.
A: Count pairs (i, j) where lower ≤ prefix[j] - prefix[i] ≤ upper. Use merge sort to count valid pairs during the merge step.
Time: O(n log n)  |  Space: O(n)
def count_range_sum(nums, lower, upper):
    prefix = [0]
    for n in nums:
        prefix.append(prefix[-1] + n)
    count = [0]
    def merge_sort(arr):
        if len(arr) <= 1:
            return arr
        mid = len(arr) // 2
        left = merge_sort(arr[:mid])
        right = merge_sort(arr[mid:])
        # Count valid pairs
        j1 = j2 = 0
        for l in left:
            while j1 < len(right) and right[j1] - l < lower:
                j1 += 1
            while j2 < len(right) and right[j2] - l <= upper:
                j2 += 1
            count[0] += j2 - j1
        # Standard merge
        return sorted(left + right)
    merge_sort(prefix)
    return count[0]

Q: What is the Maximum XOR of Two Numbers in an Array?
Write the O(n) bitwise solution in Python.
A: Build the max XOR bit by bit from MSB. For each bit, check if setting it to 1 is achievable using a prefix set and XOR property: if a^b = target, then a^target = b.
Time: O(32n)  |  Space: O(n)
def find_maximum_xor(nums):
    max_xor = 0
    mask = 0
    for i in range(31, -1, -1):
        mask |= (1 << i)
        prefixes = {n & mask for n in nums}
        candidate = max_xor | (1 << i)
        for p in prefixes:
            if p ^ candidate in prefixes:
                max_xor = candidate
                break
    return max_xor

Q: What is the Number of 1 Bits (Hamming Weight) problem?
Write multiple approaches in Python.
A: Count the number of set bits in an integer's binary representation.
Time: O(k) where k = number of set bits  |  Space: O(1)
# Brian Kernighan's: clear lowest set bit each iteration
def hamming_weight(n):
    count = 0
    while n:
        n &= n - 1
        count += 1
    return count
# Bit shift approach
def hamming_weight2(n):
    count = 0
    while n:
        count += n & 1
        n >>= 1
    return count
# Python builtin
def hamming_weight3(n):
    return bin(n).count('1')

Q: What is the Reverse Bits problem?
Write the implementation in Python.
A: Reverse all 32 bits of a given unsigned integer.
Time: O(1) (32 iterations)  |  Space: O(1)
def reverse_bits(n):
    result = 0
    for _ in range(32):
        result = (result << 1) | (n & 1)
        n >>= 1
    return result
# reverse_bits(0b00000010100101000001111010011100)
# → 0b00111001011110000010100101000000

Q: What is the Missing Number problem?
Write XOR and math solutions in Python.
A: Find the one missing number from [0, n]. Use XOR (a^a=0) or Gauss sum formula.
Time: O(n)  |  Space: O(1)
# XOR approach: XOR all indices and values
def missing_number_xor(nums):
    xor = len(nums)
    for i, num in enumerate(nums):
        xor ^= i ^ num
    return xor
# Math approach: expected - actual
def missing_number_math(nums):
    n = len(nums)
    return n * (n + 1) // 2 - sum(nums)
# missing_number([3,0,1]) → 2

Q: What is the Majority Element problem (Boyer-Moore Voting)?
Write the implementation in Python.
A: Find the element appearing more than n/2 times. Boyer-Moore: maintain a candidate and count. Increment for same, decrement for different. Majority survives.
Time: O(n)  |  Space: O(1)
def majority_element(nums):
    candidate = count = 0
    for num in nums:
        if count == 0:
            candidate = num
        count += 1 if num == candidate else -1
    return candidate
# majority_element([2,2,1,1,1,2,2]) → 2

Q: What is the Majority Element II (elements appearing > n/3 times)?
Write the implementation in Python.
A: At most 2 elements can appear more than n/3 times. Use extended Boyer-Moore with two candidates, then verify counts.
Time: O(n)  |  Space: O(1)
def majority_element_2(nums):
    c1 = c2 = 0
    r1 = r2 = None
    for n in nums:
        if r1 == n:
            c1 += 1
        elif r2 == n:
            c2 += 1
        elif c1 == 0:
            r1, c1 = n, 1
        elif c2 == 0:
            r2, c2 = n, 1
        else:
            c1 -= 1
            c2 -= 1
    # Verify
    threshold = len(nums) // 3
    return [r for r in (r1, r2) if nums.count(r) > threshold]

Q: What is the Design a Trie with Wildcard Search?
Write the implementation in Python.
A: Extend a Trie to support '.' as a wildcard matching any character. Use DFS to explore all children when '.' is encountered.
Add: O(m)  |  Search: O(m * 26) worst
class WordDictionary:
    def __init__(self):
        self.children = {}
        self.is_end = False
    def addWord(self, word):
        node = self
        for ch in word:
            if ch not in node.children:
                node.children[ch] = WordDictionary()
            node = node.children[ch]
        node.is_end = True
    def search(self, word, node=None):
        node = node or self
        for i, ch in enumerate(word):
            if ch == '.':
                return any(self.search(word[i+1:], child)
                          for child in node.children.values())
            if ch not in node.children:
                return False
            node = node.children[ch]
        return node.is_end

Q: What is the Bitwise AND of Numbers Range problem?
Write the implementation in Python.
A: Find the bitwise AND of all numbers [left, right]. The answer is the common prefix of left and right in binary, since differing lower bits will be zeroed out.
Time: O(1) (at most 32 shifts)  |  Space: O(1)
def range_bitwise_and(left, right):
    shift = 0
    while left != right:
        left >>= 1
        right >>= 1
        shift += 1
    return left << shift
# range_bitwise_and(5, 7) → 4
# 101 & 110 & 111 = 100

Q: What is the Largest Divisible Subset problem (DP)?
Write the implementation in Python.
A: Find the largest subset where every pair (a, b) satisfies a % b == 0 or b % a == 0. Sort, then LIS-style DP checking divisibility.
Time: O(n²)  |  Space: O(n)
def largest_divisible_subset(nums):
    nums.sort()
    n = len(nums)
    dp = [1] * n
    parent = [-1] * n
    max_idx = 0
    for i in range(1, n):
        for j in range(i):
            if nums[i] % nums[j] == 0 and dp[j] + 1 > dp[i]:
                dp[i] = dp[j] + 1
                parent[i] = j
        if dp[i] > dp[max_idx]:
            max_idx = i
    result = []
    while max_idx != -1:
        result.append(nums[max_idx])
        max_idx = parent[max_idx]
    return result[::-1]
# largest_divisible_subset([1,2,4,8]) → [1,2,4,8]

Q: What is the Perfect Squares problem (DP)?
Write the implementation in Python.
A: Find the minimum number of perfect squares that sum to n. Classic BFS or DP approach.
Time: O(n * √n)  |  Space: O(n)
def num_squares(n):
    dp = [float('inf')] * (n + 1)
    dp[0] = 0
    for i in range(1, n + 1):
        j = 1
        while j * j <= i:
            dp[i] = min(dp[i], dp[i - j*j] + 1)
            j += 1
    return dp[n]
# num_squares(12) → 3 (4+4+4)
# num_squares(13) → 2 (4+9)

Q: What is the Ugly Number II problem (DP)?
Write the implementation in Python.
A: Find the n-th ugly number (only prime factors 2, 3, 5). Use three pointers to generate the next ugly number by multiplying candidates.
Time: O(n)  |  Space: O(n)
def nth_ugly_number(n):
    ugly = [1]
    i2 = i3 = i5 = 0
    while len(ugly) < n:
        next2 = ugly[i2] * 2
        next3 = ugly[i3] * 3
        next5 = ugly[i5] * 5
        nxt = min(next2, next3, next5)
        ugly.append(nxt)
        if nxt == next2: i2 += 1
        if nxt == next3: i3 += 1
        if nxt == next5: i5 += 1
    return ugly[-1]
# nth_ugly_number(10) → 12
# Sequence: 1,2,3,4,5,6,8,9,10,12

Q: What is the Integer to Roman / Roman to Integer conversion?
Write both implementations in Python.
A: Map values to symbols greedily for int→roman. Map symbols to values with subtraction rule for roman→int.
Time: O(1) (bounded output)  |  Space: O(1)
def int_to_roman(num):
    vals = [(1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
            (100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
            (10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')]
    result = []
    for v, s in vals:
        while num >= v:
            result.append(s)
            num -= v
    return ''.join(result)
def roman_to_int(s):
    vals = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
    result = 0
    for i in range(len(s)):
        if i + 1 < len(s) and vals[s[i]] < vals[s[i+1]]:
            result -= vals[s[i]]
        else:
            result += vals[s[i]]
    return result

Q: What is the Basic Calculator problem (expression evaluation)?
Write the implementation in Python.
A: Evaluate a string expression with +, -, (, ). Use a stack for parentheses: push current result and sign when entering '(', pop and combine when exiting ')'.
Time: O(n)  |  Space: O(n)
def calculate(s):
    stack = []
    result = 0
    num = 0
    sign = 1
    for ch in s:
        if ch.isdigit():
            num = num * 10 + int(ch)
        elif ch in '+-':
            result += sign * num
            num = 0
            sign = 1 if ch == '+' else -1
        elif ch == '(':
            stack.append(result)
            stack.append(sign)
            result = 0
            sign = 1
        elif ch == ')':
            result += sign * num
            num = 0
            result *= stack.pop()  # sign
            result += stack.pop()  # prev result
    return result + sign * num

Q: What is the Basic Calculator II (with * and /)?
Write the implementation in Python.
A: Evaluate expression with +, -, *, / (no parentheses). Process * and / immediately using a stack, then sum the stack for + and -.
Time: O(n)  |  Space: O(n)
def calculate2(s):
    stack = []
    num = 0
    op = '+'
    for i, ch in enumerate(s):
        if ch.isdigit():
            num = num * 10 + int(ch)
        if ch in '+-*/' or i == len(s) - 1:
            if op == '+':
                stack.append(num)
            elif op == '-':
                stack.append(-num)
            elif op == '*':
                stack.append(stack.pop() * num)
            elif op == '/':
                stack.append(int(stack.pop() / num))
            op = ch
            num = 0
    return sum(stack)
# calculate2("3+2*2") → 7

Q: What is the Number of Distinct Substrings (suffix array + LCP)?
Write the implementation in Python.
A: Total substrings = n*(n+1)/2. Subtract duplicate prefixes counted by the LCP array. Distinct substrings = total - sum(LCP).
Time: O(n log²n)  |  Space: O(n)
def count_distinct_substrings(s):
    n = len(s)
    sa = build_suffix_array(s)   # from Suffix Array card
    lcp = build_lcp(s, sa)       # from Suffix Array card
    total = n * (n + 1) // 2
    return total - sum(lcp)
# "abab" → total=10, lcp=[0,2,0,1], distinct=10-3=7
# Substrings: a, ab, aba, abab, b, ba, bab

Q: What is the Smallest Range Covering Elements from K Lists?
Write the implementation in Python.
A: Find the smallest range [a,b] that includes at least one element from each of k sorted lists. Use a min-heap of size k tracking the current max.
Time: O(n log k)  |  Space: O(k)
import heapq
def smallest_range(lists):
    heap = []
    curr_max = float('-inf')
    for i, lst in enumerate(lists):
        heapq.heappush(heap, (lst[0], i, 0))
        curr_max = max(curr_max, lst[0])
    best = [float('-inf'), float('inf')]
    while True:
        curr_min, li, idx = heapq.heappop(heap)
        if curr_max - curr_min < best[1] - best[0]:
            best = [curr_min, curr_max]
        if idx + 1 == len(lists[li]):
            break
        nxt = lists[li][idx + 1]
        curr_max = max(curr_max, nxt)
        heapq.heappush(heap, (nxt, li, idx + 1))
    return best

Q: What is the Furthest Building You Can Reach problem (Greedy + Heap)?
Write the implementation in Python.
A: Use ladders for the biggest climbs and bricks for the rest. Use a min-heap of size = ladders to track the biggest climbs seen so far.
Time: O(n log L)  |  Space: O(L)
import heapq
def furthest_building(heights, bricks, ladders):
    heap = []  # min-heap of ladder-assigned climbs
    for i in range(len(heights) - 1):
        climb = heights[i + 1] - heights[i]
        if climb <= 0:
            continue
        heapq.heappush(heap, climb)
        if len(heap) > ladders:
            bricks -= heapq.heappop(heap)
        if bricks < 0:
            return i
    return len(heights) - 1

Q: What is the Zigzag Level Order Traversal of a Binary Tree?
Write the implementation in Python.
A: Like level-order but alternate direction each level: left-to-right, then right-to-left.
Time: O(n)  |  Space: O(n)
from collections import deque
def zigzag_level_order(root):
    if not root: return []
    result = []
    queue = deque([root])
    left_to_right = True
    while queue:
        level = []
        for _ in range(len(queue)):
            node = queue.popleft()
            level.append(node.val)
            if node.left: queue.append(node.left)
            if node.right: queue.append(node.right)
        if not left_to_right:
            level.reverse()
        result.append(level)
        left_to_right = not left_to_right
    return result

Q: What is the Vertical Order Traversal of a Binary Tree?
Write the implementation in Python.
A: Group nodes by column index. Use BFS with (node, row, col) and sort by column, then row, then value.
Time: O(n log n)  |  Space: O(n)
from collections import defaultdict, deque
def vertical_traversal(root):
    if not root: return []
    cols = defaultdict(list)
    queue = deque([(root, 0, 0)])
    while queue:
        node, row, col = queue.popleft()
        cols[col].append((row, node.val))
        if node.left: queue.append((node.left, row+1, col-1))
        if node.right: queue.append((node.right, row+1, col+1))
    result = []
    for col in sorted(cols):
        result.append([val for _, val in sorted(cols[col])])
    return result

Q: What is the Delete Node in a BST?
Write the implementation in Python.
A: Three cases: leaf (just remove), one child (replace with child), two children (replace with inorder successor then delete successor).
Time: O(h)  |  Space: O(h)
def delete_node(root, key):
    if not root:
        return None
    if key < root.val:
        root.left = delete_node(root.left, key)
    elif key > root.val:
        root.right = delete_node(root.right, key)
    else:
        if not root.left:
            return root.right
        if not root.right:
            return root.left
        # Find inorder successor (smallest in right subtree)
        succ = root.right
        while succ.left:
            succ = succ.left
        root.val = succ.val
        root.right = delete_node(root.right, succ.val)
    return root

Q: What is the Implement strStr() / KMP simplified?
Write the naive and optimized solutions in Python.
A: Find the first occurrence of needle in haystack. Naive: O(n*m). Use KMP or built-in for O(n+m).
# Naive O(n*m)
def str_str(haystack, needle):
    if not needle: return 0
    n, m = len(haystack), len(needle)
    for i in range(n - m + 1):
        if haystack[i:i+m] == needle:
            return i
    return -1
# Using Python builtin (uses modified Boyer-Moore)
def str_str_builtin(haystack, needle):
    return haystack.find(needle)

Q: What is the Text Justification problem?
Write the implementation in Python.
A: Pack words into lines of maxWidth, distributing extra spaces as evenly as possible (left-biased). Last line is left-justified.
Time: O(n)  |  Space: O(n)
def full_justify(words, maxWidth):
    result = []
    line = []
    line_len = 0
    for word in words:
        if line_len + len(word) + len(line) > maxWidth:
            spaces = maxWidth - line_len
            slots = max(len(line) - 1, 1)
            for i in range(spaces):
                line[i % slots] += ' '
            result.append(''.join(line))
            line = []
            line_len = 0
        line.append(word)
        line_len += len(word)
    # Last line: left-justified
    result.append(' '.join(line).ljust(maxWidth))
    return result

Q: What is the Encode and Decode Strings problem?
Write the implementation in Python.
A: Encode a list of strings into a single string and decode it back. Use length prefix: "4#word" format to handle any characters including delimiters.
Time: O(n)  |  Space: O(n)
class Codec:
    def encode(self, strs):
        return ''.join(f'{len(s)}#{s}' for s in strs)
    def decode(self, s):
        result = []
        i = 0
        while i < len(s):
            j = s.index('#', i)
            length = int(s[i:j])
            result.append(s[j+1:j+1+length])
            i = j + 1 + length
        return result
# encode(["hello","world"]) → "5#hello5#world"

Q: What is the Maximum Points on a Line problem?
Write the implementation in Python.
A: For each point, count other points with the same slope. Use a hash map with slope as (dy/gcd, dx/gcd) to avoid floating point issues.
Time: O(n²)  |  Space: O(n)
from math import gcd
from collections import defaultdict
def max_points(points):
    n = len(points)
    if n <= 2: return n
    best = 2
    for i in range(n):
        slopes = defaultdict(int)
        for j in range(i + 1, n):
            dx = points[j][0] - points[i][0]
            dy = points[j][1] - points[i][1]
            g = gcd(abs(dx), abs(dy))
            if g: dx, dy = dx // g, dy // g
            if dx < 0: dx, dy = -dx, -dy
            if dx == 0: dy = abs(dy)
            slopes[(dx, dy)] += 1
        if slopes:
            best = max(best, max(slopes.values()) + 1)
    return best

Q: What is the Monotonic Queue pattern?
Write the implementation in Python.
A: A deque maintaining monotonic order (increasing or decreasing) to efficiently track min/max in a sliding window. Elements are removed from the back when they violate the order.
Time: O(n) amortized  |  Space: O(k)
from collections import deque
# Sliding window minimum using monotonic increasing deque
def sliding_window_min(nums, k):
    dq = deque()  # stores indices
    result = []
    for i, num in enumerate(nums):
        while dq and dq[0] <= i - k:
            dq.popleft()
        while dq and nums[dq[-1]] >= num:
            dq.pop()
        dq.append(i)
        if i >= k - 1:
            result.append(nums[dq[0]])
    return result
# sliding_window_min([1,3,-1,-3,5,3,6,7], 3)
# → [-1,-3,-3,-3,3,3]

Q: What is the Single Number II (every element appears 3 times except one)?
Write the implementation in Python.
A: Use bitwise state machine with two variables (ones, twos) to count bits mod 3.
Time: O(n)  |  Space: O(1)
def single_number_ii(nums):
    ones = twos = 0
    for n in nums:
        ones = (ones ^ n) & ~twos
        twos = (twos ^ n) & ~ones
    return ones
# General approach: count each bit mod 3
def single_number_ii_general(nums):
    result = 0
    for i in range(32):
        bit_sum = sum((n >> i) & 1 for n in nums)
        if bit_sum % 3:
            result |= 1 << i
    if result >= 2**31:
        result -= 2**32
    return result
# single_number_ii([2,2,3,2]) → 3

Q: What is the Single Number III (two unique elements)?
Write the implementation in Python.
A: XOR all numbers to get a^b. Find a distinguishing bit (lowest set bit) and partition numbers into two groups. XOR each group to recover a and b.
Time: O(n)  |  Space: O(1)
def single_number_iii(nums):
    xor = 0
    for n in nums:
        xor ^= n
    # Lowest set bit (differs between a and b)
    diff_bit = xor & (-xor)
    a = b = 0
    for n in nums:
        if n & diff_bit:
            a ^= n
        else:
            b ^= n
    return [a, b]
# single_number_iii([1,2,1,3,2,5]) → [3,5]

Q: What is the Word Search II (Trie + Backtracking)?
Write the implementation in Python.
A: Find all words from a dictionary in a 2D board. Build a Trie from words, then DFS backtrack on the board matching Trie paths.
Time: O(m*n * 4^L)  |  Space: O(W*L) for Trie
def find_words(board, words):
    trie = {}
    for w in words:
        node = trie
        for ch in w:
            node = node.setdefault(ch, {})
        node['#'] = w
    m, n = len(board), len(board[0])
    result = []
    def dfs(r, c, node):
        ch = board[r][c]
        if ch not in node: return
        nxt = node[ch]
        if '#' in nxt:
            result.append(nxt.pop('#'))
        board[r][c] = '.'
        for dr, dc in ((0,1),(0,-1),(1,0),(-1,0)):
            nr, nc = r+dr, c+dc
            if 0 <= nr < m and 0 <= nc < n and board[nr][nc] != '.':
                dfs(nr, nc, nxt)
        board[r][c] = ch
        if not nxt: del node[ch]  # prune
    for r in range(m):
        for c in range(n):
            dfs(r, c, trie)
    return result

Q: What is the Next Permutation algorithm?
Write the implementation in Python.
A: Rearrange numbers into the lexicographically next greater permutation. Find rightmost ascent i (a[i] < a[i+1]), swap a[i] with the smallest element to its right that is larger, then reverse suffix.
Time: O(n)  |  Space: O(1)
def next_permutation(nums):
    n = len(nums)
    # Step 1: find rightmost ascent
    i = n - 2
    while i >= 0 and nums[i] >= nums[i + 1]:
        i -= 1
    if i >= 0:
        # Step 2: find rightmost element > nums[i]
        j = n - 1
        while nums[j] <= nums[i]:
            j -= 1
        nums[i], nums[j] = nums[j], nums[i]
    # Step 3: reverse suffix
    nums[i+1:] = nums[i+1:][::-1]
# [1,2,3] → [1,3,2]
# [3,2,1] → [1,2,3]
# [1,1,5] → [1,5,1]

Q: What is the Kth Permutation Sequence (factoradic)?
Write the implementation in Python.
A: Find the k-th permutation of [1..n] directly using factorial number system. Repeatedly pick digit = (k-1) / (n-1)! and remove it.
Time: O(n²)  |  Space: O(n)
from math import factorial
def get_permutation(n, k):
    numbers = list(range(1, n + 1))
    k -= 1  # 0-indexed
    result = []
    for i in range(n, 0, -1):
        f = factorial(i - 1)
        idx = k // f
        result.append(str(numbers[idx]))
        numbers.pop(idx)
        k %= f
    return ''.join(result)
# get_permutation(3, 3) → "213"
# get_permutation(4, 9) → "2314"

Q: What is the Minimum Number of Platforms problem?
Write the implementation in Python.
A: Find the maximum overlapping intervals (events). Sort arrivals and departures separately, use two pointers to sweep through events.
Time: O(n log n)  |  Space: O(n)
def min_platforms(arrivals, departures):
    arrivals.sort()
    departures.sort()
    platforms = max_platforms = 0
    i = j = 0
    while i < len(arrivals):
        if arrivals[i] <= departures[j]:
            platforms += 1
            max_platforms = max(max_platforms, platforms)
            i += 1
        else:
            platforms -= 1
            j += 1
    return max_platforms
# Equivalent: line sweep with +1/-1 events

Q: What is the Stock Buy/Sell with At Most K Transactions?
Write the implementation in Python.
A: DP with states: dp[t][i] = max profit using at most t transactions up to day i. Optimize with a running max of dp[t-1][j] - price[j].
Time: O(nk)  |  Space: O(nk)
def max_profit_k(k, prices):
    n = len(prices)
    if not n: return 0
    if k >= n // 2:  # unlimited transactions
        return sum(max(0, prices[i+1]-prices[i]) for i in range(n-1))
    dp = [[0] * n for _ in range(k + 1)]
    for t in range(1, k + 1):
        max_prev = -prices[0]
        for i in range(1, n):
            dp[t][i] = max(dp[t][i-1], prices[i] + max_prev)
            max_prev = max(max_prev, dp[t-1][i] - prices[i])
    return dp[k][n-1]

Q: What is the Stock Buy/Sell with Cooldown?
Write the implementation in Python.
A: State machine DP: hold (have stock), sold (just sold), rest (cooldown/idle). Transitions enforce the one-day cooldown after selling.
Time: O(n)  |  Space: O(1)
def max_profit_cooldown(prices):
    hold = float('-inf')  # holding stock
    sold = 0              # just sold
    rest = 0              # cooldown
    for p in prices:
        prev_sold = sold
        sold = hold + p
        hold = max(hold, rest - p)
        rest = max(rest, prev_sold)
    return max(sold, rest)
# [1,2,3,0,2] → 3 (buy@1 sell@2, buy@0 sell@2)

Q: What is the Wildcard Matching problem?
Write the DP implementation in Python.
A: Match string s against pattern p with '?' (any single char) and '*' (any sequence including empty). 2D DP where dp[i][j] = s[:i] matches p[:j].
Time: O(m*n)  |  Space: O(m*n)
def is_match(s, p):
    m, n = len(s), len(p)
    dp = [[False] * (n + 1) for _ in range(m + 1)]
    dp[0][0] = True
    for j in range(1, n + 1):
        if p[j-1] == '*':
            dp[0][j] = dp[0][j-1]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if p[j-1] == '*':
                dp[i][j] = dp[i-1][j] or dp[i][j-1]
            elif p[j-1] == '?' or s[i-1] == p[j-1]:
                dp[i][j] = dp[i-1][j-1]
    return dp[m][n]
# is_match("adceb", "*a*b") → True

Q: What is the Word Break II (return all sentences)?
Write the implementation in Python.
A: Find all ways to segment a string into dictionary words. Use memoized DFS to build sentences from the end.
Time: O(n * 2^n) worst  |  Space: O(n * 2^n)
def word_break_ii(s, wordDict):
    wordSet = set(wordDict)
    memo = {}
    def dfs(start):
        if start in memo: return memo[start]
        if start == len(s): return [[]]
        result = []
        for end in range(start + 1, len(s) + 1):
            word = s[start:end]
            if word in wordSet:
                for rest in dfs(end):
                    result.append([word] + rest)
        memo[start] = result
        return result
    return [' '.join(words) for words in dfs(0)]
# word_break_ii("catsanddog", ["cat","cats","and","sand","dog"])
# → ["cats and dog", "cat sand dog"]

Q: What is the Interleaving String problem (DP)?
Write the implementation in Python.
A: Check if s3 is formed by interleaving s1 and s2 (maintaining order). 2D DP where dp[i][j] = s1[:i] and s2[:j] can form s3[:i+j].
Time: O(m*n)  |  Space: O(n) with optimization
def is_interleave(s1, s2, s3):
    m, n = len(s1), len(s2)
    if m + n != len(s3): return False
    dp = [False] * (n + 1)
    for i in range(m + 1):
        for j in range(n + 1):
            if i == 0 and j == 0:
                dp[j] = True
            elif i == 0:
                dp[j] = dp[j-1] and s2[j-1] == s3[j-1]
            elif j == 0:
                dp[j] = dp[j] and s1[i-1] == s3[i-1]
            else:
                dp[j] = (dp[j] and s1[i-1] == s3[i+j-1]) or \
                        (dp[j-1] and s2[j-1] == s3[i+j-1])
    return dp[n]

Q: What is the Distinct Subsequences problem (DP)?
Write the implementation in Python.
A: Count the number of distinct subsequences of s that equal t. dp[i][j] = ways to form t[:j] from s[:i].
Time: O(m*n)  |  Space: O(n)
def num_distinct(s, t):
    m, n = len(s), len(t)
    dp = [0] * (n + 1)
    dp[0] = 1  # empty string matches
    for i in range(1, m + 1):
        for j in range(n, 0, -1):  # reverse to avoid overwrite
            if s[i-1] == t[j-1]:
                dp[j] += dp[j-1]
    return dp[n]
# num_distinct("rabbbit", "rabbit") → 3
# num_distinct("babgbag", "bag") → 5

Q: What is the Burst Balloons problem (interval DP)?
Write the implementation in Python.
A: Find max coins from bursting all balloons. Interval DP: dp[l][r] = max coins from bursting balloons in (l, r). Choose the last balloon to burst in each interval.
Time: O(n³)  |  Space: O(n²)
def max_coins(nums):
    nums = [1] + nums + [1]
    n = len(nums)
    dp = [[0] * n for _ in range(n)]
    for length in range(2, n):  # gap between l and r
        for l in range(0, n - length):
            r = l + length
            for k in range(l + 1, r):  # last to burst
                dp[l][r] = max(dp[l][r],
                    dp[l][k] + dp[k][r] +
                    nums[l] * nums[k] * nums[r])
    return dp[0][n - 1]
# max_coins([3,1,5,8]) → 167

Q: What is the Super Egg Drop problem?
Write the optimized DP implementation in Python.
A: Find the minimum number of moves to determine the critical floor with k eggs and n floors. DP on moves: dp[m][k] = max floors checkable with m moves and k eggs.
Time: O(k * log n)  |  Space: O(nk)
def super_egg_drop(k, n):
    # dp[m][k] = max floors we can check with m moves, k eggs
    dp = [[0] * (k + 1) for _ in range(n + 1)]
    m = 0
    while dp[m][k] < n:
        m += 1
        for j in range(1, k + 1):
            dp[m][j] = dp[m-1][j-1] + dp[m-1][j] + 1
            # egg breaks: check below (m-1 moves, j-1 eggs)
            # egg survives: check above (m-1 moves, j eggs)
            # +1 for current floor
    return m
# super_egg_drop(2, 100) → 14
# super_egg_drop(1, 100) → 100

Q: What is the Frog Jump problem (DP)?
Write the implementation in Python.
A: A frog crosses a river by jumping on stones. If last jump was k, next can be k-1, k, or k+1. Use a set-based DP tracking possible jump sizes at each stone.
Time: O(n²)  |  Space: O(n²)
def can_cross(stones):
    stone_set = {s: set() for s in stones}
    stone_set[0].add(0)
    for stone in stones:
        for k in stone_set[stone]:
            for jump in (k-1, k, k+1):
                if jump > 0 and stone + jump in stone_set:
                    stone_set[stone + jump].add(jump)
    return len(stone_set[stones[-1]]) > 0
# can_cross([0,1,3,5,6,8,12,17]) → True
# can_cross([0,1,2,3,4,8,9,11]) → False

Q: What is the Minimum Cost to Merge Stones (interval DP)?
Write the implementation in Python.
A: Merge n piles into 1, merging exactly K consecutive piles at a time. Cost = sum of merged piles. Only possible if (n-1) % (K-1) == 0.
Time: O(n³/K)  |  Space: O(n²)
def merge_stones(stones, K):
    n = len(stones)
    if (n - 1) % (K - 1) != 0: return -1
    prefix = [0] * (n + 1)
    for i in range(n):
        prefix[i+1] = prefix[i] + stones[i]
    INF = float('inf')
    dp = [[0]*n for _ in range(n)]
    for length in range(K, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            dp[i][j] = INF
            for mid in range(i, j, K - 1):
                dp[i][j] = min(dp[i][j], dp[i][mid] + dp[mid+1][j])
            if (length - 1) % (K - 1) == 0:
                dp[i][j] += prefix[j+1] - prefix[i]
    return dp[0][n-1]

Q: What is the Longest String Chain problem?
Write the implementation in Python.
A: Find the longest chain where each word is a predecessor (differs by removing one char) of the next. Sort by length, then DP with hash map.
Time: O(n * L²)  |  Space: O(n)
def longest_str_chain(words):
    words.sort(key=len)
    dp = {}  # word → longest chain ending at word
    best = 1
    for word in words:
        dp[word] = 1
        for i in range(len(word)):
            pred = word[:i] + word[i+1:]
            if pred in dp:
                dp[word] = max(dp[word], dp[pred] + 1)
        best = max(best, dp[word])
    return best
# longest_str_chain(["a","b","ba","bca","bda","bdca"]) → 4
# Chain: "a" → "ba" → "bda" → "bdca"

Q: What is the Minimum Falling Path Sum (DP)?
Write the implementation in Python.
A: Find the path from top to bottom row of a matrix with minimum sum, moving to adjacent columns (j-1, j, j+1) each row.
Time: O(n²)  |  Space: O(n)
def min_falling_path_sum(matrix):
    n = len(matrix)
    dp = matrix[0][:]
    for i in range(1, n):
        new_dp = [0] * n
        for j in range(n):
            left = dp[j-1] if j > 0 else float('inf')
            mid = dp[j]
            right = dp[j+1] if j < n-1 else float('inf')
            new_dp[j] = matrix[i][j] + min(left, mid, right)
        dp = new_dp
    return min(dp)
# min_falling_path_sum([[2,1,3],[6,5,4],[7,8,9]]) → 13
# Path: 1 → 5 → 7

Q: What is the Minimum Swaps to Sort an Array?
Write the implementation in Python.
A: Find minimum swaps to sort. Build a graph of cycles: each element points to its correct position. Each cycle of length k needs k-1 swaps.
Time: O(n log n)  |  Space: O(n)
def min_swaps_to_sort(arr):
    n = len(arr)
    indexed = sorted(enumerate(arr), key=lambda x: x[1])
    visited = [False] * n
    swaps = 0
    for i in range(n):
        if visited[i] or indexed[i][0] == i:
            continue
        cycle_size = 0
        j = i
        while not visited[j]:
            visited[j] = True
            j = indexed[j][0]
            cycle_size += 1
        swaps += cycle_size - 1
    return swaps
# min_swaps_to_sort([4,3,2,1]) → 2
# min_swaps_to_sort([1,5,4,3,2]) → 2

Q: What is the Minimum Window Substring with order (Minimum Window Subsequence)?
Write the DP implementation in Python.
A: Find the minimum window in S that contains T as a subsequence (order matters). DP: dp[j] = starting index in S where T[:j] is a subsequence ending at current position.
Time: O(m*n)  |  Space: O(n)
def min_window_subseq(S, T):
    m, n = len(S), len(T)
    best = ""
    best_len = float('inf')
    # dp[j] = start index of T[:j] as subsequence
    dp = [-1] * (n + 1)
    dp[0] = 0  # empty T always matches
    for i in range(m):
        # traverse T backwards to avoid overwrite
        for j in range(n, 0, -1):
            if S[i] == T[j-1] and dp[j-1] != -1:
                dp[j] = dp[j-1]
        if dp[n] != -1:
            length = i - dp[n] + 1
            if length < best_len:
                best_len = length
                best = S[dp[n]:i+1]
            dp[n] = -1  # reset to find next
    return best

Q: What is the Longest Arithmetic Subsequence?
Write the implementation in Python.
A: Find the length of the longest subsequence with a constant difference. Use DP with hash maps: dp[i][d] = length of arithmetic subseq ending at i with difference d.
Time: O(n²)  |  Space: O(n²)
def longest_arith_seq(nums):
    n = len(nums)
    dp = [{} for _ in range(n)]  # dp[i] = {diff: length}
    best = 2
    for i in range(n):
        for j in range(i):
            d = nums[i] - nums[j]
            dp[i][d] = dp[j].get(d, 1) + 1
            best = max(best, dp[i][d])
    return best
# longest_arith_seq([3,6,9,12]) → 4
# longest_arith_seq([9,4,7,2,10]) → 3 (4,7,10)

Q: What is the Maximum Length of Pair Chain problem?
Write the implementation in Python.
A: Select the longest chain of pairs (a,b) where for consecutive pairs (c,d), b < c. Classic interval scheduling: sort by end, greedily pick non-overlapping.
Time: O(n log n)  |  Space: O(1)
def find_longest_chain(pairs):
    pairs.sort(key=lambda x: x[1])
    count = 0
    end = float('-inf')
    for a, b in pairs:
        if a > end:
            count += 1
            end = b
    return count
# find_longest_chain([[1,2],[2,3],[3,4]]) → 2
# Pick [1,2],[3,4]

Q: What is the Orderly Queue problem?
Write the implementation in Python.
A: Given a string and integer k. In each move, pick one of the first k characters and append it to the end. If k >= 2, any permutation is reachable (return sorted string). If k == 1, try all rotations.
Time: O(n²) for k=1, O(n log n) for k≥2  |  Space: O(n)
def orderly_queue(s, k):
    if k >= 2:
        return ''.join(sorted(s))
    # k == 1: find lexicographically smallest rotation
    best = s
    for i in range(1, len(s)):
        rotation = s[i:] + s[:i]
        if rotation < best:
            best = rotation
    return best
# orderly_queue("cba", 1) → "acb"
# orderly_queue("cba", 2) → "abc"

Q: What is the Gray Code generation algorithm?
Write the implementation in Python.
A: Generate n-bit Gray code sequence where consecutive numbers differ by exactly one bit. Use the formula: gray(i) = i ^ (i >> 1).
Time: O(2^n)  |  Space: O(2^n)
# Formula: i XOR (i >> 1)
def gray_code(n):
    return [i ^ (i >> 1) for i in range(1 << n)]
# Recursive (mirror approach)
def gray_code_recursive(n):
    if n == 0: return [0]
    prev = gray_code_recursive(n - 1)
    mask = 1 << (n - 1)
    return prev + [x | mask for x in reversed(prev)]
# gray_code(3) → [0,1,3,2,6,7,5,4]
# Binary: 000,001,011,010,110,111,101,100

Q: What is the Count of Smaller Numbers After Self (merge sort)?
Write the implementation in Python.
A: For each element, count elements to its right that are smaller. Use merge sort and count inversions during the merge step.
Time: O(n log n)  |  Space: O(n)
def count_smaller(nums):
    n = len(nums)
    result = [0] * n
    indices = list(range(n))
    def merge_sort(arr):
        if len(arr) <= 1: return arr
        mid = len(arr) // 2
        left = merge_sort(arr[:mid])
        right = merge_sort(arr[mid:])
        merged = []
        i = j = 0
        right_count = 0
        while i < len(left) and j < len(right):
            if nums[right[j]] < nums[left[i]]:
                right_count += 1
                merged.append(right[j])
                j += 1
            else:
                result[left[i]] += right_count
                merged.append(left[i])
                i += 1
        while i < len(left):
            result[left[i]] += right_count
            merged.append(left[i])
            i += 1
        merged.extend(right[j:])
        return merged
    merge_sort(indices)
    return result
# count_smaller([5,2,6,1]) → [2,1,1,0]

Q: What is the Topological Sort using Kahn's Algorithm (BFS)?
Write the implementation in Python.
A: Process nodes with in-degree 0 first using a queue. For each processed node, reduce in-degree of neighbors. If all nodes processed, no cycle exists.
Time: O(V + E)  |  Space: O(V + E)
from collections import deque, defaultdict
def topological_sort_kahn(n, edges):
    graph = defaultdict(list)
    in_degree = [0] * n
    for u, v in edges:
        graph[u].append(v)
        in_degree[v] += 1
    queue = deque(i for i in range(n) if in_degree[i] == 0)
    order = []
    while queue:
        node = queue.popleft()
        order.append(node)
        for nei in graph[node]:
            in_degree[nei] -= 1
            if in_degree[nei] == 0:
                queue.append(nei)
    return order if len(order) == n else []  # empty = cycle

Q: What is the 0/1 BFS (shortest path on 0-1 weighted graph)?
Write the implementation in Python.
A: Use a deque instead of a priority queue. Push 0-weight edges to front, 1-weight edges to back. Achieves O(V+E) instead of O(E log V).
Time: O(V + E)  |  Space: O(V)
from collections import deque
def bfs_01(graph, src, n):
    dist = [float('inf')] * n
    dist[src] = 0
    dq = deque([src])
    while dq:
        u = dq.popleft()
        for v, w in graph[u]:  # w is 0 or 1
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                if w == 0:
                    dq.appendleft(v)
                else:
                    dq.append(v)
    return dist

Q: What is the Minimum Spanning Tree check using DFS (verify if a tree)?
Write the implementation in Python.
A: A graph with n nodes is a spanning tree if it has exactly n-1 edges, is connected, and has no cycles. Verify with DFS.
Time: O(V + E)  |  Space: O(V)
def is_valid_tree(n, edges):
    if len(edges) != n - 1:
        return False
    adj = [[] for _ in range(n)]
    for u, v in edges:
        adj[u].append(v)
        adj[v].append(u)
    visited = set()
    def dfs(node, parent):
        visited.add(node)
        for nei in adj[node]:
            if nei == parent: continue
            if nei in visited: return False
            if not dfs(nei, node): return False
        return True
    return dfs(0, -1) and len(visited) == n

Q: What is the Multi-Source BFS pattern?
Write the implementation in Python.
A: Start BFS from all sources simultaneously by pushing them all into the queue initially. Computes shortest distance from any source to all cells.
Time: O(V + E)  |  Space: O(V)
from collections import deque
def multi_source_bfs(grid, sources):
    m, n = len(grid), len(grid[0])
    dist = [[float('inf')] * n for _ in range(m)]
    queue = deque()
    for r, c in sources:
        dist[r][c] = 0
        queue.append((r, c))
    while queue:
        r, c = queue.popleft()
        for dr, dc in ((0,1),(0,-1),(1,0),(-1,0)):
            nr, nc = r+dr, c+dc
            if 0 <= nr < m and 0 <= nc < n and dist[nr][nc] == float('inf'):
                dist[nr][nc] = dist[r][c] + 1
                queue.append((nr, nc))
    return dist
# Use case: "01 Matrix" - distance to nearest 0 for each cell

Q: What is the Bidirectional BFS optimization?
Write the implementation in Python.
A: Run BFS from both start and end simultaneously. Expand the smaller frontier each step. Meet in the middle reduces search space from O(b^d) to O(b^(d/2)).
Time: O(b^(d/2))  |  Space: O(b^(d/2))
def bidirectional_bfs(begin, end, word_list):
    word_set = set(word_list)
    if end not in word_set: return 0
    front = {begin}
    back = {end}
    visited = {begin, end}
    steps = 1
    while front and back:
        if len(front) > len(back):
            front, back = back, front
        next_front = set()
        for word in front:
            for i in range(len(word)):
                for c in 'abcdefghijklmnopqrstuvwxyz':
                    nw = word[:i] + c + word[i+1:]
                    if nw in back:
                        return steps + 1
                    if nw in word_set and nw not in visited:
                        visited.add(nw)
                        next_front.add(nw)
        front = next_front
        steps += 1
    return 0

Q: What is the Cycle Detection in Undirected Graph using Union-Find?
Write the implementation in Python.
A: For each edge (u, v): if find(u) == find(v), adding this edge creates a cycle. Otherwise, union them.
Time: O(E * α(V)) ≈ O(E)  |  Space: O(V)
def has_cycle_uf(n, edges):
    parent = list(range(n))
    rank = [0] * n
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x
    def union(a, b):
        ra, rb = find(a), find(b)
        if ra == rb: return False  # cycle!
        if rank[ra] < rank[rb]: ra, rb = rb, ra
        parent[rb] = ra
        if rank[ra] == rank[rb]: rank[ra] += 1
        return True
    for u, v in edges:
        if not union(u, v):
            return True
    return False

Q: What is the Shortest Path in a Grid with Obstacles Elimination?
Write the BFS implementation in Python.
A: BFS on state (row, col, remaining_eliminations). Track visited with the elimination count to prune redundant states.
Time: O(m*n*k)  |  Space: O(m*n*k)
from collections import deque
def shortest_path(grid, k):
    m, n = len(grid), len(grid[0])
    if m == 1 and n == 1: return 0
    queue = deque([(0, 0, k, 0)])  # row, col, elims left, steps
    visited = set()
    visited.add((0, 0, k))
    while queue:
        r, c, e, steps = queue.popleft()
        for dr, dc in ((0,1),(0,-1),(1,0),(-1,0)):
            nr, nc = r+dr, c+dc
            if 0 <= nr < m and 0 <= nc < n:
                ne = e - grid[nr][nc]
                if nr == m-1 and nc == n-1:
                    return steps + 1
                if ne >= 0 and (nr, nc, ne) not in visited:
                    visited.add((nr, nc, ne))
                    queue.append((nr, nc, ne, steps + 1))
    return -1

Q: What is the Cheapest Flights Within K Stops (Bellman-Ford variant)?
Write the implementation in Python.
A: Find cheapest price from src to dst with at most k stops. Run Bellman-Ford for exactly k+1 relaxation rounds, copying prices each round to avoid cascading updates.
Time: O(k * E)  |  Space: O(V)
def find_cheapest_price(n, flights, src, dst, k):
    prices = [float('inf')] * n
    prices[src] = 0
    for _ in range(k + 1):
        tmp = prices[:]
        for u, v, w in flights:
            if prices[u] != float('inf'):
                tmp[v] = min(tmp[v], prices[u] + w)
        prices = tmp
    return prices[dst] if prices[dst] != float('inf') else -1
# find_cheapest_price(4, [[0,1,100],[1,2,100],[0,2,500]],
#                     0, 2, 1) → 200

Q: What is the Network Delay Time (Dijkstra application)?
Write the implementation in Python.
A: Find the time for a signal to reach all nodes from a source. Run Dijkstra and return the maximum shortest distance. If any node unreachable, return -1.
Time: O(E log V)  |  Space: O(V + E)
import heapq
from collections import defaultdict
def network_delay(times, n, k):
    graph = defaultdict(list)
    for u, v, w in times:
        graph[u].append((v, w))
    dist = {}
    heap = [(0, k)]
    while heap:
        d, u = heapq.heappop(heap)
        if u in dist: continue
        dist[u] = d
        for v, w in graph[u]:
            if v not in dist:
                heapq.heappush(heap, (d + w, v))
    return max(dist.values()) if len(dist) == n else -1

Q: What is the Redundant Connection problem (find the cycle edge)?
Write the implementation in Python.
A: In an undirected graph that is a tree + one extra edge, find the edge that creates a cycle. Use Union-Find: the first edge where both endpoints share a root is redundant.
Time: O(n * α(n))  |  Space: O(n)
def find_redundant_connection(edges):
    n = len(edges)
    parent = list(range(n + 1))
    rank = [0] * (n + 1)
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x
    for u, v in edges:
        ru, rv = find(u), find(v)
        if ru == rv:
            return [u, v]
        if rank[ru] < rank[rv]: ru, rv = rv, ru
        parent[rv] = ru
        if rank[ru] == rank[rv]: rank[ru] += 1
    return []

Q: What is the Accounts Merge problem (Union-Find on strings)?
Write the implementation in Python.
A: Merge accounts that share any email. Use Union-Find on email → account index, then group merged emails by root.
Time: O(n * k * α(nk))  |  Space: O(nk)
from collections import defaultdict
def accounts_merge(accounts):
    parent = {}
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x
    def union(a, b):
        parent[find(a)] = find(b)
    email_to_name = {}
    for acct in accounts:
        name = acct[0]
        for email in acct[1:]:
            if email not in parent:
                parent[email] = email
            email_to_name[email] = name
            union(acct[1], email)
    groups = defaultdict(list)
    for email in parent:
        groups[find(email)].append(email)
    return [[email_to_name[root]] + sorted(emails)
            for root, emails in groups.items()]

Q: What is the Number of Connected Components (Union-Find)?
Write the implementation in Python.
A: Count connected components in an undirected graph. Start with n components, each union reduces count by 1.
Time: O(E * α(V))  |  Space: O(V)
def count_components(n, edges):
    parent = list(range(n))
    rank = [0] * n
    components = n
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x
    def union(a, b):
        nonlocal components
        ra, rb = find(a), find(b)
        if ra == rb: return
        if rank[ra] < rank[rb]: ra, rb = rb, ra
        parent[rb] = ra
        if rank[ra] == rank[rb]: rank[ra] += 1
        components -= 1
    for u, v in edges:
        union(u, v)
    return components

Q: What is the All O(1) Data Structure (Inc/Dec/Max/Min in O(1))?
Write the implementation in Python.
A: Use a doubly-linked list of buckets (each bucket = a count + set of keys) plus a hash map key→bucket. Inc/Dec move keys between adjacent buckets.
All ops: O(1)
class Node:
    def __init__(self, count=0):
        self.count = count
        self.keys = set()
        self.prev = self.next = None
class AllOne:
    def __init__(self):
        self.head = Node()  # sentinel min
        self.tail = Node()  # sentinel max
        self.head.next = self.tail
        self.tail.prev = self.head
        self.key_node = {}  # key → Node
    def _insert_after(self, prev_node, count):
        node = Node(count)
        node.prev, node.next = prev_node, prev_node.next
        prev_node.next.prev = node
        prev_node.next = node
        return node
    def _remove(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev
    def inc(self, key):
        if key in self.key_node:
            node = self.key_node[key]
            new_count = node.count + 1
            nxt = node.next
            if nxt == self.tail or nxt.count != new_count:
                nxt = self._insert_after(node, new_count)
            nxt.keys.add(key)
            node.keys.discard(key)
            if not node.keys: self._remove(node)
            self.key_node[key] = nxt
        else:
            nxt = self.head.next
            if nxt == self.tail or nxt.count != 1:
                nxt = self._insert_after(self.head, 1)
            nxt.keys.add(key)
            self.key_node[key] = nxt
    def get_max_key(self):
        return next(iter(self.tail.prev.keys)) if self.tail.prev != self.head else ""
    def get_min_key(self):
        return next(iter(self.head.next.keys)) if self.head.next != self.tail else ""

Q: What is the Implement Queue using Two Stacks?
Write the amortized O(1) implementation in Python.
A: Use an input stack and output stack. Push to input. For pop/peek, transfer all from input to output only when output is empty. Each element moves at most twice → amortized O(1).
class MyQueue:
    def __init__(self):
        self.stack_in = []
        self.stack_out = []
    def push(self, x):
        self.stack_in.append(x)
    def pop(self):
        self._move()
        return self.stack_out.pop()
    def peek(self):
        self._move()
        return self.stack_out[-1]
    def empty(self):
        return not self.stack_in and not self.stack_out
    def _move(self):
        if not self.stack_out:
            while self.stack_in:
                self.stack_out.append(self.stack_in.pop())

Q: What is the Implement Stack using Two Queues?
Write the implementation in Python.
A: Push: add to queue, then rotate all previous elements behind it. This makes the newest element always at the front. Pop/top are O(1).
Push: O(n)  |  Pop: O(1)
from collections import deque
class MyStack:
    def __init__(self):
        self.q = deque()
    def push(self, x):
        self.q.append(x)
        for _ in range(len(self.q) - 1):
            self.q.append(self.q.popleft())
    def pop(self):
        return self.q.popleft()
    def top(self):
        return self.q[0]
    def empty(self):
        return not self.q

Q: What is the Circular Queue (Ring Buffer) implementation?
Write the implementation in Python.
A: Fixed-size queue using an array with head and tail pointers wrapping around via modulo. Supports O(1) enqueue, dequeue, front, rear.
class MyCircularQueue:
    def __init__(self, k):
        self.data = [0] * k
        self.size = k
        self.head = 0
        self.count = 0
    def enQueue(self, value):
        if self.isFull(): return False
        tail = (self.head + self.count) % self.size
        self.data[tail] = value
        self.count += 1
        return True
    def deQueue(self):
        if self.isEmpty(): return False
        self.head = (self.head + 1) % self.size
        self.count -= 1
        return True
    def Front(self):
        return -1 if self.isEmpty() else self.data[self.head]
    def Rear(self):
        if self.isEmpty(): return -1
        tail = (self.head + self.count - 1) % self.size
        return self.data[tail]
    def isEmpty(self): return self.count == 0
    def isFull(self): return self.count == self.size

Q: What is the Design HashMap from scratch?
Write the implementation in Python.
A: Use an array of buckets with chaining (linked list or list). Hash function maps key → bucket index. Handle collisions via chaining.
Average: O(1)  |  Worst: O(n/k)
class MyHashMap:
    def __init__(self, size=1000):
        self.size = size
        self.buckets = [[] for _ in range(size)]
    def _hash(self, key):
        return key % self.size
    def put(self, key, value):
        bucket = self.buckets[self._hash(key)]
        for i, (k, v) in enumerate(bucket):
            if k == key:
                bucket[i] = (key, value)
                return
        bucket.append((key, value))
    def get(self, key):
        bucket = self.buckets[self._hash(key)]
        for k, v in bucket:
            if k == key: return v
        return -1
    def remove(self, key):
        bucket = self.buckets[self._hash(key)]
        for i, (k, v) in enumerate(bucket):
            if k == key:
                bucket.pop(i)
                return

Q: What is the Design a Min Heap from scratch?
Write the implementation in Python.
A: Array-based binary heap. Parent of i is (i-1)//2, children are 2i+1 and 2i+2. Sift up on insert, sift down on extract-min.
Insert: O(log n)  |  Extract-min: O(log n)  |  Peek: O(1)
class MinHeap:
    def __init__(self):
        self.heap = []
    def push(self, val):
        self.heap.append(val)
        self._sift_up(len(self.heap) - 1)
    def pop(self):
        self.heap[0], self.heap[-1] = self.heap[-1], self.heap[0]
        val = self.heap.pop()
        if self.heap: self._sift_down(0)
        return val
    def peek(self): return self.heap[0]
    def _sift_up(self, i):
        while i > 0:
            parent = (i - 1) // 2
            if self.heap[i] < self.heap[parent]:
                self.heap[i], self.heap[parent] = self.heap[parent], self.heap[i]
                i = parent
            else: break
    def _sift_down(self, i):
        n = len(self.heap)
        while True:
            smallest = i
            l, r = 2*i+1, 2*i+2
            if l < n and self.heap[l] < self.heap[smallest]: smallest = l
            if r < n and self.heap[r] < self.heap[smallest]: smallest = r
            if smallest == i: break
            self.heap[i], self.heap[smallest] = self.heap[smallest], self.heap[i]
            i = smallest

Q: What is the Median Maintenance (running median) with two heaps?
Write the implementation in Python.
A: Maintain a max-heap for the lower half and a min-heap for the upper half. Balance sizes so they differ by at most 1. Median is from the larger heap or average of both tops.
Insert: O(log n)  |  Find median: O(1)
import heapq
class MedianMaintenance:
    def __init__(self):
        self.lo = []  # max-heap (negate values)
        self.hi = []  # min-heap
    def add(self, num):
        heapq.heappush(self.lo, -num)
        heapq.heappush(self.hi, -heapq.heappop(self.lo))
        if len(self.hi) > len(self.lo):
            heapq.heappush(self.lo, -heapq.heappop(self.hi))
    def median(self):
        if len(self.lo) > len(self.hi):
            return -self.lo[0]
        return (-self.lo[0] + self.hi[0]) / 2

Q: What is the Iterator Pattern for Flatten Nested List?
Write the implementation in Python.
A: Lazily flatten a nested structure using a stack. On hasNext(), keep unpacking lists until a plain integer is on top.
hasNext/next: O(1) amortized  |  Space: O(d) depth
class NestedIterator:
    def __init__(self, nestedList):
        self.stack = list(reversed(nestedList))
    def next(self):
        return self.stack.pop().getInteger()
    def hasNext(self):
        while self.stack:
            top = self.stack[-1]
            if top.isInteger():
                return True
            self.stack.pop()
            self.stack.extend(reversed(top.getList()))
        return False
# Usage:
# it = NestedIterator([[1,1],2,[1,1]])
# while it.hasNext(): print(it.next())
# → 1, 1, 2, 1, 1

Q: What is the Random Shuffle (Fisher-Yates / Knuth Shuffle)?
Write the implementation in Python.
A: Iterate from the end; swap each element with a random element from index 0 to i (inclusive). Produces a uniformly random permutation.
Time: O(n)  |  Space: O(1)
import random
def shuffle(arr):
    arr = arr[:]
    for i in range(len(arr) - 1, 0, -1):
        j = random.randint(0, i)
        arr[i], arr[j] = arr[j], arr[i]
    return arr
# Verify uniformity: each of n! permutations
# is equally likely

Q: What is the Weighted Random Selection (Alias Method)?
Write the simple prefix-sum approach in Python.
A: Select an index with probability proportional to its weight. Build prefix sums and binary search a random value in [0, total).
Build: O(n)  |  Pick: O(log n)
import random
import bisect
class WeightedRandom:
    def __init__(self, weights):
        self.prefix = []
        total = 0
        for w in weights:
            total += w
            self.prefix.append(total)
        self.total = total
    def pick(self):
        target = random.random() * self.total
        return bisect.bisect_left(self.prefix, target)
# wr = WeightedRandom([1, 3, 2])
# wr.pick() → 0 (1/6), 1 (3/6), 2 (2/6)

Q: What is the Pow(x, n) — Fast Exponentiation?
Write the implementation in Python.
A: Compute x^n in O(log n) using exponentiation by squaring. If n is even: x^n = (x²)^(n/2). If odd: x^n = x * x^(n-1).
Time: O(log n)  |  Space: O(1) iterative
# Iterative
def my_pow(x, n):
    if n < 0:
        x, n = 1/x, -n
    result = 1
    while n > 0:
        if n % 2 == 1:
            result *= x
        x *= x
        n //= 2
    return result
# Recursive
def my_pow_rec(x, n):
    if n == 0: return 1
    if n < 0: return my_pow_rec(1/x, -n)
    half = my_pow_rec(x, n // 2)
    return half * half * (x if n % 2 else 1)

Q: What is the GCD / LCM using Euclidean Algorithm?
Write the implementation in Python.
A: GCD: repeatedly replace (a,b) with (b, a%b) until b=0. LCM = a*b / gcd(a,b). Extended GCD finds coefficients x,y such that ax + by = gcd(a,b).
Time: O(log(min(a,b)))
def gcd(a, b):
    while b:
        a, b = b, a % b
    return a
def lcm(a, b):
    return a * b // gcd(a, b)
# Extended GCD: returns (g, x, y) where ax + by = g
def extended_gcd(a, b):
    if b == 0:
        return a, 1, 0
    g, x, y = extended_gcd(b, a % b)
    return g, y, x - (a // b) * y
# from math import gcd, lcm  # Python 3.9+

Q: What is the Modular Exponentiation and Modular Inverse?
Write the implementation in Python.
A: Compute (base^exp) % mod efficiently. Modular inverse of a mod m exists if gcd(a,m)=1: a^(-1) ≡ a^(m-2) mod m (Fermat's little theorem for prime m).
Time: O(log exp)
def mod_pow(base, exp, mod):
    result = 1
    base %= mod
    while exp > 0:
        if exp % 2 == 1:
            result = result * base % mod
        exp //= 2
        base = base * base % mod
    return result
# Python builtin: pow(base, exp, mod)
def mod_inverse(a, mod):
    # Only works when mod is prime
    return pow(a, mod - 2, mod)
# nCr mod p (precompute factorials)
def ncr_mod(n, r, p):
    if r > n: return 0
    fac = [1] * (n + 1)
    for i in range(1, n + 1):
        fac[i] = fac[i-1] * i % p
    return fac[n] * mod_inverse(fac[r], p) % p * mod_inverse(fac[n-r], p) % p

Q: What is the Catalan Number computation?
Write the implementation in Python.
A: The n-th Catalan number counts: valid parentheses, BSTs with n nodes, triangulations, etc. C(n) = C(2n,n)/(n+1) or DP: C(n) = Σ C(i)*C(n-1-i).
Time: O(n) formula or O(n²) DP
# Direct formula: C(n) = (2n)! / ((n+1)! * n!)
def catalan(n):
    from math import comb
    return comb(2 * n, n) // (n + 1)
# DP approach
def catalan_dp(n):
    dp = [0] * (n + 1)
    dp[0] = dp[1] = 1
    for i in range(2, n + 1):
        for j in range(i):
            dp[i] += dp[j] * dp[i - 1 - j]
    return dp[n]
# catalan(5) → 42
# First 8: 1, 1, 2, 5, 14, 42, 132, 429

Q: What is the Inclusion-Exclusion Principle (count elements in union)?
Write the implementation in Python.
A: Count elements in the union of sets: add single sets, subtract pairwise intersections, add triple intersections, etc. Used for counting multiples, derangements, etc.
Time: O(2^n) for n sets
# Count integers in [1, n] divisible by any prime in primes
def count_divisible(n, primes):
    total = 0
    m = len(primes)
    for mask in range(1, 1 << m):
        product = 1
        bits = 0
        for i in range(m):
            if mask & (1 << i):
                product *= primes[i]
                bits += 1
        if bits % 2 == 1:
            total += n // product
        else:
            total -= n // product
    return total
# count_divisible(30, [2, 3, 5])
# = 15+10+6 - 5-3-2 + 1 = 22

Q: 303. What is Bucket Sort and what is its time complexity?
Write the implementation in Python.
A: Bucket Sort distributes elements into buckets, sorts each bucket individually, then concatenates. Best for uniformly distributed data.
Time: O(n + k) avg, O(n²) worst  |  Space: O(n + k)
def bucket_sort(arr, num_buckets=10):
    if not arr:
        return arr
    min_val, max_val = min(arr), max(arr)
    bucket_range = (max_val - min_val) / num_buckets + 1
    buckets = [[] for _ in range(num_buckets)]
    for x in arr:
        idx = int((x - min_val) / bucket_range)
        buckets[idx].append(x)
    result = []
    for bucket in buckets:
        result.extend(sorted(bucket))
    return result
# bucket_sort([0.42, 0.32, 0.23, 0.52, 0.25, 0.47])

Q: 304. What is Shell Sort and what is its time complexity?
Write the implementation in Python.
A: Shell Sort is a generalization of insertion sort that allows swapping far-apart elements using a decreasing gap sequence.
Time: O(n log²n) to O(n²) depending on gap sequence  |  Space: O(1)
def shell_sort(arr):
    n = len(arr)
    gap = n // 2
    while gap > 0:
        for i in range(gap, n):
            temp = arr[i]
            j = i
            while j >= gap and arr[j - gap] > temp:
                arr[j] = arr[j - gap]
                j -= gap
            arr[j] = temp
        gap //= 2
    return arr
# shell_sort([12, 34, 54, 2, 3]) → [2, 3, 12, 34, 54]

Q: 305. How do you merge two sorted linked lists?
Write the implementation in Python.
A: Use a dummy head and compare nodes from both lists, appending the smaller one each time.
Time: O(n + m)  |  Space: O(1)
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
def merge_two_lists(l1, l2):
    dummy = ListNode(0)
    curr = dummy
    while l1 and l2:
        if l1.val <= l2.val:
            curr.next = l1
            l1 = l1.next
        else:
            curr.next = l2
            l2 = l2.next
        curr = curr.next
    curr.next = l1 or l2
    return dummy.next

Q: 306. How do you check if a linked list is a palindrome?
Write the O(1) space solution in Python.
A: Find the middle with slow/fast pointers, reverse the second half, then compare both halves.
Time: O(n)  |  Space: O(1)
def is_palindrome(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    # Reverse second half
    prev = None
    while slow:
        slow.next, prev, slow = prev, slow, slow.next
    # Compare halves
    left, right = head, prev
    while right:
        if left.val != right.val:
            return False
        left = left.next
        right = right.next
    return True

Q: 307. How do you rotate a linked list by K places?
Write the implementation in Python.
A: Find the length, connect tail to head forming a ring, then break at position length - k % length.
Time: O(n)  |  Space: O(1)
def rotate_right(head, k):
    if not head or not head.next or k == 0:
        return head
    # Find length and tail
    length = 1
    tail = head
    while tail.next:
        tail = tail.next
        length += 1
    k %= length
    if k == 0:
        return head
    # Connect tail to head
    tail.next = head
    # Find new tail at (length - k) steps from head
    steps = length - k
    new_tail = head
    for _ in range(steps - 1):
        new_tail = new_tail.next
    new_head = new_tail.next
    new_tail.next = None
    return new_head

Q: 308. What is the Find the Duplicate Number problem (Floyd's on array)?
Write the implementation in Python.
A: Treat the array as a linked list where arr[i] points to arr[arr[i]]. Use Floyd's cycle detection to find the duplicate entry.
Time: O(n)  |  Space: O(1)
def find_duplicate(nums):
    # Phase 1: Find intersection point
    slow = fast = nums[0]
    while True:
        slow = nums[slow]
        fast = nums[nums[fast]]
        if slow == fast:
            break
    # Phase 2: Find cycle entrance
    slow = nums[0]
    while slow != fast:
        slow = nums[slow]
        fast = nums[fast]
    return slow
# find_duplicate([1,3,4,2,2]) → 2

Q: 309. What is the Maximum Sum Circular Subarray problem?
Write the implementation in Python.
A: The max circular subarray is either a normal subarray (Kadane's) or wraps around: total_sum - min_subarray. Return the max of both.
Time: O(n)  |  Space: O(1)
def max_subarray_circular(nums):
    max_sum = cur_max = float('-inf')
    min_sum = cur_min = float('inf')
    total = 0
    for x in nums:
        cur_max = max(x, cur_max + x)
        max_sum = max(max_sum, cur_max)
        cur_min = min(x, cur_min + x)
        min_sum = min(min_sum, cur_min)
        total += x
    # If all negative, max_sum is the answer
    if total == min_sum:
        return max_sum
    return max(max_sum, total - min_sum)
# max_subarray_circular([5, -3, 5]) → 10

Q: 310. What is the Difference Array technique for range updates?
Write the implementation in Python.
A: To add a value to a range [l, r], increment diff[l] and decrement diff[r+1]. Prefix sum of the diff array gives the final array.
Update: O(1) per range  |  Build: O(n)
class DifferenceArray:
    def __init__(self, n):
        self.diff = [0] * (n + 1)
        self.n = n
    def range_add(self, l, r, val):
        self.diff[l] += val
        if r + 1 <= self.n:
            self.diff[r + 1] -= val
    def build(self):
        result = [0] * self.n
        result[0] = self.diff[0]
        for i in range(1, self.n):
            result[i] = result[i-1] + self.diff[i]
        return result
# d = DifferenceArray(5)
# d.range_add(1, 3, 2)  # add 2 to indices 1..3
# d.range_add(2, 4, 3)  # add 3 to indices 2..4
# d.build() → [0, 2, 5, 5, 3]

Q: 311. What is the Matrix Chain Multiplication problem (DP)?
Write the implementation in Python.
A: Find the optimal way to parenthesize matrix multiplications to minimize total scalar multiplications. Classic interval DP.
Time: O(n³)  |  Space: O(n²)
def matrix_chain_order(dims):
    n = len(dims) - 1
    dp = [[0] * n for _ in range(n)]
    # l = chain length
    for l in range(2, n + 1):
        for i in range(n - l + 1):
            j = i + l - 1
            dp[i][j] = float('inf')
            for k in range(i, j):
                cost = (dp[i][k] + dp[k+1][j]
                        + dims[i] * dims[k+1] * dims[j+1])
                dp[i][j] = min(dp[i][j], cost)
    return dp[0][n-1]
# dims = [10, 20, 30, 40]
# Matrices: 10x20, 20x30, 30x40
# matrix_chain_order(dims) → 18000

Q: 312. What is the Rod Cutting problem (DP)?
Write the implementation in Python.
A: Given a rod of length n and a price table, find the maximum revenue by cutting the rod into pieces.
Time: O(n²)  |  Space: O(n)
def rod_cutting(prices, n):
    dp = [0] * (n + 1)
    for i in range(1, n + 1):
        for j in range(1, i + 1):
            if j <= len(prices):
                dp[i] = max(dp[i], prices[j-1] + dp[i-j])
    return dp[n]
# With cut reconstruction
def rod_cutting_with_cuts(prices, n):
    dp = [0] * (n + 1)
    cuts = [0] * (n + 1)
    for i in range(1, n + 1):
        for j in range(1, i + 1):
            if j <= len(prices) and prices[j-1] + dp[i-j] > dp[i]:
                dp[i] = prices[j-1] + dp[i-j]
                cuts[i] = j
    # Reconstruct
    result = []
    while n > 0:
        result.append(cuts[n])
        n -= cuts[n]
    return dp[sum(result)], result
# prices = [1, 5, 8, 9, 10, 17, 17, 20]
# rod_cutting(prices, 8) → 22 (cuts: 2+6)

Q: 313. What is the Painter's Partition / Book Allocation problem (Binary Search on Answer)?
Write the implementation in Python.
A: Minimize the maximum pages assigned to any student when distributing books among k students. Binary search on the answer and greedily check feasibility.
Time: O(n log S) where S = sum of pages
def allocate_books(pages, k):
    def is_feasible(max_pages):
        students = 1
        current = 0
        for p in pages:
            if current + p > max_pages:
                students += 1
                current = p
                if students > k:
                    return False
            else:
                current += p
        return True
    if k > len(pages):
        return -1
    lo, hi = max(pages), sum(pages)
    while lo < hi:
        mid = (lo + hi) // 2
        if is_feasible(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo
# allocate_books([12, 34, 67, 90], 2) → 113

Q: 314. What is the Activity Selection problem (Greedy)?
Write the implementation in Python.
A: Select the maximum number of non-overlapping activities. Sort by finish time and greedily pick the earliest finishing activity.
Time: O(n log n)  |  Space: O(1)
def activity_selection(activities):
    # activities = [(start, end), ...]
    activities.sort(key=lambda x: x[1])
    selected = [activities[0]]
    last_end = activities[0][1]
    for start, end in activities[1:]:
        if start >= last_end:
            selected.append((start, end))
            last_end = end
    return selected
# activities = [(1,4),(3,5),(0,6),(5,7),(3,9),(5,9),(6,10),(8,11),(8,12),(2,14),(12,16)]
# activity_selection(activities)
# → [(1,4),(5,7),(8,11),(12,16)]

Q: 315. What is the Fractional Knapsack problem (Greedy)?
Write the implementation in Python.
A: Unlike 0/1 knapsack, you can take fractions of items. Sort by value/weight ratio and greedily pick.
Time: O(n log n)  |  Space: O(1)
def fractional_knapsack(items, capacity):
    # items = [(value, weight), ...]
    items.sort(key=lambda x: x[0]/x[1], reverse=True)
    total_value = 0
    for value, weight in items:
        if capacity >= weight:
            total_value += value
            capacity -= weight
        else:
            total_value += value * (capacity / weight)
            break
    return total_value
# items = [(60,10), (100,20), (120,30)]
# fractional_knapsack(items, 50) → 240.0

Q: 316. What is Huffman Coding (Greedy)?
Write the implementation in Python.
A: Build an optimal prefix-free binary code by repeatedly merging the two lowest-frequency nodes into a tree.
Time: O(n log n)  |  Space: O(n)
import heapq
class HuffNode:
    def __init__(self, char, freq, left=None, right=None):
        self.char = char
        self.freq = freq
        self.left = left
        self.right = right
    def __lt__(self, other):
        return self.freq < other.freq
def huffman_codes(freq_map):
    heap = [HuffNode(c, f) for c, f in freq_map.items()]
    heapq.heapify(heap)
    while len(heap) > 1:
        lo = heapq.heappop(heap)
        hi = heapq.heappop(heap)
        merged = HuffNode(None, lo.freq + hi.freq, lo, hi)
        heapq.heappush(heap, merged)
    codes = {}
    def dfs(node, code):
        if node.char is not None:
            codes[node.char] = code or '0'
            return
        dfs(node.left, code + '0')
        dfs(node.right, code + '1')
    dfs(heap[0], '')
    return codes
# huffman_codes({'a':5,'b':9,'c':12,'d':13,'e':16,'f':45})
# → {'f':'0','c':'100','d':'101','a':'1100','b':'1101','e':'111'}

Q: 317. What is Bitmask DP (Traveling Salesman Problem)?
Write the implementation in Python.
A: Use a bitmask to represent visited nodes. dp[mask][i] = min cost to visit nodes in mask, ending at i.
Time: O(2^n · n²)  |  Space: O(2^n · n)
def tsp(dist):
    n = len(dist)
    INF = float('inf')
    dp = [[INF] * n for _ in range(1 << n)]
    dp[1][0] = 0  # start at node 0
    for mask in range(1 << n):
        for u in range(n):
            if dp[mask][u] == INF:
                continue
            for v in range(n):
                if mask & (1 << v):
                    continue
                new_mask = mask | (1 << v)
                dp[new_mask][v] = min(
                    dp[new_mask][v],
                    dp[mask][u] + dist[u][v]
                )
    full = (1 << n) - 1
    return min(dp[full][i] + dist[i][0] for i in range(n))
# dist = [[0,10,15,20],[10,0,35,25],
#         [15,35,0,30],[20,25,30,0]]
# tsp(dist) → 80

Q: 318. What is Digit DP (Count numbers with a digit property)?
Write the implementation in Python.
A: Count numbers from 0 to N satisfying a digit constraint using DP over digit positions. Track tight bound, position, and state.
Time: O(D × S × 10) where D=digits, S=states
from functools import lru_cache
def count_numbers_with_digit_sum(N, target_sum):
    digits = list(map(int, str(N)))
    n = len(digits)
    @lru_cache(maxsize=None)
    def dp(pos, cur_sum, tight):
        if cur_sum > target_sum:
            return 0
        if pos == n:
            return 1 if cur_sum == target_sum else 0
        limit = digits[pos] if tight else 9
        total = 0
        for d in range(0, limit + 1):
            total += dp(pos + 1, cur_sum + d,
                       tight and d == limit)
        return total
    return dp(0, 0, True)
# Count numbers in [0, 100] with digit sum = 5
# count_numbers_with_digit_sum(100, 5) → 6
# (5, 14, 23, 32, 41, 50)

Q: 319. What is Tree DP (Maximum Independent Set on a tree)?
Write the implementation in Python.
A: For each node, compute two values: best answer including this node and excluding it. Recurse on children.
Time: O(n)  |  Space: O(n)
def max_independent_set(adj, weights, root=0):
    n = len(adj)
    inc = [0] * n  # include this node
    exc = [0] * n  # exclude this node
    visited = [False] * n
    def dfs(u):
        visited[u] = True
        inc[u] = weights[u]
        exc[u] = 0
        for v in adj[u]:
            if not visited[v]:
                dfs(v)
                inc[u] += exc[v]
                exc[u] += max(inc[v], exc[v])
    dfs(root)
    return max(inc[root], exc[root])
# Tree: 0-1, 0-2, 1-3, 1-4
# adj = [[1,2],[0,3,4],[0],[1],[1]]
# weights = [1, 6, 3, 4, 5]
# max_independent_set(adj, weights) → 12 (nodes 2,3,4)

Q: 320. What is the Chinese Remainder Theorem?
Write the implementation in Python.
A: Given a system x ≡ r_i (mod m_i) with pairwise coprime moduli, find the unique solution modulo M = ∏m_i.
Time: O(n log M)
def extended_gcd(a, b):
    if b == 0:
        return a, 1, 0
    g, x, y = extended_gcd(b, a % b)
    return g, y, x - (a // b) * y
def chinese_remainder(remainders, moduli):
    M = 1
    for m in moduli:
        M *= m
    result = 0
    for r, m in zip(remainders, moduli):
        Mi = M // m
        _, inv, _ = extended_gcd(Mi, m)
        result += r * Mi * inv
    return result % M
# x ≡ 2 (mod 3), x ≡ 3 (mod 5), x ≡ 2 (mod 7)
# chinese_remainder([2, 3, 5], [3, 5, 7]) → 68
# Verify: 68%3=2, 68%5=3, 68%7=5 ✓

Q: 321. What is the Josephus Problem?
Write the implementation in Python.
A: N people in a circle; every k-th person is eliminated. Find the position of the survivor. Recursive formula: J(n,k) = (J(n-1,k) + k) % n.
Time: O(n)  |  Space: O(1)
def josephus(n, k):
    # Returns 0-indexed position of survivor
    result = 0
    for i in range(2, n + 1):
        result = (result + k) % i
    return result
# Recursive version
def josephus_rec(n, k):
    if n == 1:
        return 0
    return (josephus_rec(n - 1, k) + k) % n
# josephus(7, 3) → 3 (0-indexed)
# 7 people, every 3rd eliminated
# Elimination order: 2,5,1,6,4,0 → survivor at index 3

Q: 322. What is the Tower of Hanoi problem?
Write the implementation in Python.
A: Move n disks from source to target using an auxiliary peg. Move n-1 disks to aux, move largest to target, move n-1 from aux to target.
Time: O(2^n)  |  Moves: 2^n - 1
def hanoi(n, source='A', target='C', auxiliary='B'):
    moves = []
    def solve(n, src, tgt, aux):
        if n == 0:
            return
        solve(n - 1, src, aux, tgt)
        moves.append((src, tgt))
        solve(n - 1, aux, tgt, src)
    solve(n, source, target, auxiliary)
    return moves
# hanoi(3)
# → [('A','C'),('A','B'),('C','B'),
#    ('A','C'),('B','A'),('B','C'),('A','C')]
# 7 moves for 3 disks

Q: 323. What is the Jump Game II problem (minimum jumps)?
Write the greedy solution in Python.
A: Track the farthest reachable position and the current window end. Increment jumps when the window end is reached.
Time: O(n)  |  Space: O(1)
def jump(nums):
    jumps = 0
    current_end = 0
    farthest = 0
    for i in range(len(nums) - 1):
        farthest = max(farthest, i + nums[i])
        if i == current_end:
            jumps += 1
            current_end = farthest
            if current_end >= len(nums) - 1:
                break
    return jumps
# jump([2,3,1,1,4]) → 2
# Jump 1: index 0 → 1, Jump 2: index 1 → 4

Q: 324. What is Polynomial Rolling Hash for string matching?
Write the implementation in Python.
A: Hash a string as a polynomial: h = Σ s[i]·p^i mod m. Use rolling updates to compute hash of next window in O(1).
Precompute: O(n)  |  Query: O(1)
class RollingHash:
    def __init__(self, s, base=31, mod=10**9+9):
        self.mod = mod
        self.base = base
        n = len(s)
        self.h = [0] * (n + 1)
        self.pw = [1] * (n + 1)
        for i in range(n):
            self.h[i+1] = (self.h[i] * base + ord(s[i])) % mod
            self.pw[i+1] = self.pw[i] * base % mod
    def get_hash(self, l, r):
        # Hash of s[l:r+1]
        return (self.h[r+1] - self.h[l] * self.pw[r-l+1]) % self.mod
def find_pattern(text, pattern):
    rh_t = RollingHash(text)
    rh_p = RollingHash(pattern)
    ph = rh_p.get_hash(0, len(pattern)-1)
    m = len(pattern)
    return [i for i in range(len(text)-m+1)
            if rh_t.get_hash(i, i+m-1) == ph]
# find_pattern("abcabcabc", "abc") → [0, 3, 6]

Q: 325. What is Karatsuba Multiplication?
Write the implementation in Python.
A: Multiply two n-digit numbers using 3 multiplications instead of 4 by the identity: xy = z2·B² + (z1-z2-z0)·B + z0.
Time: O(n^1.585) vs O(n²) for schoolbook
def karatsuba(x, y):
    if x < 10 or y < 10:
        return x * y
    n = max(len(str(x)), len(str(y)))
    half = n // 2
    B = 10 ** half
    a, b = divmod(x, B)  # x = a*B + b
    c, d = divmod(y, B)  # y = c*B + d
    z0 = karatsuba(b, d)
    z2 = karatsuba(a, c)
    z1 = karatsuba(a + b, c + d)
    return z2 * B * B + (z1 - z2 - z0) * B + z0
# karatsuba(1234, 5678) → 7006652
# Only 3 recursive multiplications per level

Q: 326. What is the Line Sweep algorithm (Skyline Problem)?
Write the implementation in Python.
A: Process events sorted by x-coordinate. Use a max-heap to track active building heights and detect skyline changes.
Time: O(n log n)  |  Space: O(n)
import heapq
def get_skyline(buildings):
    events = []
    for left, right, height in buildings:
        events.append((left, -height, right))  # start
        events.append((right, 0, 0))            # end
    events.sort()
    result = []
    heap = [(0, float('inf'))]  # (-height, end_x)
    prev_max = 0
    for x, neg_h, end in events:
        if neg_h:
            heapq.heappush(heap, (neg_h, end))
        # Remove expired buildings
        while heap[0][1] <= x:
            heapq.heappop(heap)
        cur_max = -heap[0][0]
        if cur_max != prev_max:
            result.append([x, cur_max])
            prev_max = cur_max
    return result
# get_skyline([(2,9,10),(3,7,15),(5,12,12)])
# → [[2,10],[3,15],[7,12],[12,0]]

Q: 327. What is the Convex Hull Trick for DP optimization?
Write the implementation in Python.
A: Optimizes DP recurrences of the form dp[i] = min(b[j] + a[i]·m[j]) by maintaining a lower envelope of lines.
Query: O(log n) or O(1) amortized with pointer
class ConvexHullTrick:
    def __init__(self):
        self.lines = []  # (slope, intercept)
    def bad(self, l1, l2, l3):
        # Is l2 unnecessary given l1 and l3?
        return ((l3[1]-l1[1]) * (l1[0]-l2[0])
                <= (l2[1]-l1[1]) * (l1[0]-l3[0]))
    def add_line(self, m, b):
        line = (m, b)
        while (len(self.lines) >= 2 and
               self.bad(self.lines[-2], self.lines[-1], line)):
            self.lines.pop()
        self.lines.append(line)
    def query(self, x):
        lo, hi = 0, len(self.lines) - 1
        while lo < hi:
            mid = (lo + hi) // 2
            if self.eval(mid, x) > self.eval(mid+1, x):
                lo = mid + 1
            else:
                hi = mid
        return self.eval(lo, x)
    def eval(self, idx, x):
        m, b = self.lines[idx]
        return m * x + b
# cht = ConvexHullTrick()
# cht.add_line(1, 1)  # y = x + 1
# cht.add_line(-1, 5) # y = -x + 5
# cht.query(3) → 2 (min of 4, 2)

Q: 328. What is the Range Update and Point Query using BIT (Fenwick Tree)?
Write the implementation in Python.
A: Reverse the BIT logic: update a range by adding to two points, query a point by prefix sum. Dual of standard BIT.
Update: O(log n)  |  Query: O(log n)
class BIT_RangeUpdate:
    def __init__(self, n):
        self.n = n
        self.tree = [0] * (n + 1)
    def _update(self, i, delta):
        while i <= self.n:
            self.tree[i] += delta
            i += i & (-i)
    def range_add(self, l, r, val):
        self._update(l, val)
        if r + 1 <= self.n:
            self._update(r + 1, -val)
    def point_query(self, i):
        s = 0
        while i > 0:
            s += self.tree[i]
            i -= i & (-i)
        return s
# bit = BIT_RangeUpdate(5)
# bit.range_add(2, 4, 3)  # add 3 to positions 2..4
# [bit.point_query(i) for i in range(1, 6)]
# → [0, 3, 3, 3, 0]

Q: 329. What is Count Set Bits from 1 to N?
Write the implementation in Python.
A: Count total set bits in all numbers from 1 to N. Use the pattern: for each bit position, count how many numbers have that bit set.
Time: O(log n)  |  Space: O(1)
def count_set_bits(n):
    if n < 0:
        return 0
    total = 0
    power_of_2 = 1
    while power_of_2 <= n:
        total_pairs = (n + 1) // (power_of_2 * 2)
        total += total_pairs * power_of_2
        remainder = (n + 1) % (power_of_2 * 2)
        total += max(0, remainder - power_of_2)
        power_of_2 <<= 1
    return total
# count_set_bits(7) → 12
# 1:1, 2:1, 3:2, 4:1, 5:2, 6:2, 7:3 → total=12
# Brute force check:
# sum(bin(i).count('1') for i in range(1, 8)) → 12

Q: 330. What is the Pancake Sort algorithm?
Write the implementation in Python.
A: Sort by only using prefix reversals (flip the first k elements). Find the max, flip it to front, then flip it to its correct position.
Time: O(n²)  |  Space: O(1)
def pancake_sort(arr):
    def flip(k):
        arr[:k+1] = arr[:k+1][::-1]
    flips = []
    n = len(arr)
    for size in range(n, 1, -1):
        max_idx = arr.index(max(arr[:size]))
        if max_idx == size - 1:
            continue
        if max_idx != 0:
            flip(max_idx)
            flips.append(max_idx + 1)
        flip(size - 1)
        flips.append(size)
    return flips
# arr = [3, 2, 4, 1]
# pancake_sort(arr)
# flips: [3, 4, 2, 3, 2] → arr becomes [1, 2, 3, 4]

Q: 331. What is the Cycle Sort algorithm?
Write the implementation in Python.
A: Minimizes writes by placing each element directly at its correct position. Count elements smaller to find the target index.
Time: O(n²)  |  Space: O(1)  |  Writes: O(n)
def cycle_sort(arr):
    writes = 0
    for start in range(len(arr) - 1):
        item = arr[start]
        pos = start
        for i in range(start + 1, len(arr)):
            if arr[i] < item:
                pos += 1
        if pos == start:
            continue
        while item == arr[pos]:
            pos += 1
        arr[pos], item = item, arr[pos]
        writes += 1
        while pos != start:
            pos = start
            for i in range(start + 1, len(arr)):
                if arr[i] < item:
                    pos += 1
            while item == arr[pos]:
                pos += 1
            arr[pos], item = item, arr[pos]
            writes += 1
    return writes
# arr = [4, 3, 2, 1]
# cycle_sort(arr) → 2 writes, arr = [1, 2, 3, 4]

Q: 332. What is the Minimum Number of Coins (Greedy) problem?
Write the implementation in Python.
A: Given canonical coin denominations (where greedy works), always pick the largest coin ≤ remaining. For arbitrary denominations, use DP (Coin Change).
Time: O(n) greedy  |  Space: O(1)
# Greedy (only for canonical denominations)
def min_coins_greedy(coins, amount):
    coins.sort(reverse=True)
    count = 0
    result = []
    for coin in coins:
        while amount >= coin:
            amount -= coin
            count += 1
            result.append(coin)
    return count if amount == 0 else -1
# DP (works for any denominations)
def min_coins_dp(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for i in range(1, amount + 1):
        for c in coins:
            if c <= i:
                dp[i] = min(dp[i], dp[i - c] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1
# min_coins_greedy([25, 10, 5, 1], 41) → 4 (25+10+5+1)
# min_coins_dp([1, 3, 4], 6) → 2 (3+3)

Q: 333. What is the Unbounded Knapsack problem (DP)?
Write the implementation in Python.
A: Unlike 0/1 knapsack, each item can be used unlimited times. Use a 1D DP array where dp[w] = max value with capacity w.
Time: O(n·W)  |  Space: O(W)
def unbounded_knapsack(weights, values, W):
    dp = [0] * (W + 1)
    for w in range(1, W + 1):
        for i in range(len(weights)):
            if weights[i] <= w:
                dp[w] = max(dp[w], dp[w - weights[i]] + values[i])
    return dp[W]
# weights = [1, 3, 4, 5]
# values  = [10, 40, 50, 70]
# unbounded_knapsack(weights, values, 8) → 110
# (two items of weight 4 → 50+50, or weight 3+5 → 40+70=110)

Q: 334. What is the Minimum Edit Distance with allowed operations (Insert, Delete, Replace)?
Write the space-optimized solution in Python.
A: Reduce the standard O(mn) space to O(min(m,n)) by only keeping two rows of the DP table at a time.
Time: O(m·n)  |  Space: O(min(m,n))
def edit_distance_optimized(s1, s2):
    if len(s1) < len(s2):
        s1, s2 = s2, s1
    m, n = len(s1), len(s2)
    prev = list(range(n + 1))
    for i in range(1, m + 1):
        curr = [i] + [0] * n
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                curr[j] = prev[j-1]
            else:
                curr[j] = 1 + min(prev[j],     # delete
                                  curr[j-1],    # insert
                                  prev[j-1])    # replace
        prev = curr
    return prev[n]
# edit_distance_optimized("saturday", "sunday") → 3

Q: 335. What is the Longest Bitonic Subsequence problem (DP)?
Write the implementation in Python.
A: A bitonic subsequence first increases then decreases. Compute LIS from left and LIS from right, then maximize lis[i]+lds[i]-1.
Time: O(n²)  |  Space: O(n)
def longest_bitonic(arr):
    n = len(arr)
    # LIS ending at each index (left to right)
    lis = [1] * n
    for i in range(1, n):
        for j in range(i):
            if arr[j] < arr[i]:
                lis[i] = max(lis[i], lis[j] + 1)
    # LDS ending at each index (right to left)
    lds = [1] * n
    for i in range(n - 2, -1, -1):
        for j in range(n - 1, i, -1):
            if arr[j] < arr[i]:
                lds[i] = max(lds[i], lds[j] + 1)
    return max(lis[i] + lds[i] - 1 for i in range(n))
# longest_bitonic([1, 11, 2, 10, 4, 5, 2, 1]) → 6
# Subsequence: 1, 2, 10, 4, 2, 1

Q: 336. What is the Wiggle Sort problem?
Write the O(n) implementation in Python.
A: Rearrange array so that a[0] ≤ a[1] ≥ a[2] ≤ a[3] ≥ ... Swap adjacent elements when the wiggle property is violated.
Time: O(n)  |  Space: O(1)
# Wiggle Sort I (allow equal neighbors)
def wiggle_sort(nums):
    for i in range(1, len(nums)):
        if (i % 2 == 1 and nums[i] < nums[i-1]) or \
           (i % 2 == 0 and nums[i] > nums[i-1]):
            nums[i], nums[i-1] = nums[i-1], nums[i]
# Wiggle Sort II (strict inequality, O(n) with median)
def wiggle_sort_ii(nums):
    import statistics
    med = statistics.median(nums)
    n = len(nums)
    arr = sorted(nums)
    small = arr[:n//2 + n%2][::-1]
    large = arr[n//2 + n%2:][::-1]
    for i in range(n):
        nums[i] = small[i//2] if i % 2 == 0 else large[i//2]
# nums = [3, 5, 2, 1, 6, 4]
# wiggle_sort(nums) → [3, 5, 1, 6, 2, 4]

Q: 337. What is the K-th Element of Two Sorted Arrays?
Write the O(log(min(m,n))) solution in Python.
A: Generalize Median of Two Sorted Arrays: binary search on the smaller array to partition both arrays such that exactly k elements are on the left.
Time: O(log(min(m,n)))
def kth_element(arr1, arr2, k):
    if len(arr1) > len(arr2):
        return kth_element(arr2, arr1, k)
    n1, n2 = len(arr1), len(arr2)
    lo = max(0, k - n2)
    hi = min(k, n1)
    while lo <= hi:
        cut1 = (lo + hi) // 2
        cut2 = k - cut1
        l1 = arr1[cut1-1] if cut1 > 0 else float('-inf')
        l2 = arr2[cut2-1] if cut2 > 0 else float('-inf')
        r1 = arr1[cut1] if cut1 < n1 else float('inf')
        r2 = arr2[cut2] if cut2 < n2 else float('inf')
        if l1 <= r2 and l2 <= r1:
            return max(l1, l2)
        elif l1 > r2:
            hi = cut1 - 1
        else:
            lo = cut1 + 1
# kth_element([2,3,6,7,9], [1,4,8,10], 5) → 6

Q: 338. What is the Minimum Cost Path in a Grid (DP)?
Write the space-optimized solution in Python.
A: Find the min-cost path from top-left to bottom-right moving only right or down. Optimize to O(n) space using a single row.
Time: O(m·n)  |  Space: O(n)
def min_cost_path(grid):
    m, n = len(grid), len(grid[0])
    dp = [float('inf')] * n
    dp[0] = 0
    for i in range(m):
        dp[0] = dp[0] + grid[i][0]
        for j in range(1, n):
            if i == 0:
                dp[j] = dp[j-1] + grid[i][j]
            else:
                dp[j] = min(dp[j], dp[j-1]) + grid[i][j]
    return dp[n-1]
# grid = [[1, 3, 1],
#         [1, 5, 1],
#         [4, 2, 1]]
# min_cost_path(grid) → 7 (1→3→1→1→1)

Q: 339. What is the Count of Palindromic Substrings problem?
Write the implementation in Python.
A: Expand around each center (single char and between chars). Each valid expansion is a palindrome. Alternatively use Manacher's.
Time: O(n²)  |  Space: O(1)
def count_palindromes(s):
    count = 0
    n = len(s)
    def expand(l, r):
        nonlocal count
        while l >= 0 and r < n and s[l] == s[r]:
            count += 1
            l -= 1
            r += 1
    for i in range(n):
        expand(i, i)      # odd length
        expand(i, i + 1)  # even length
    return count
# count_palindromes("aab") → 4
# "a", "a", "b", "aa"
# count_palindromes("abc") → 3 (each single char)

Q: 340. What is the Strongly Connected Components count using Tarjan's (simplified)?
Write a clean implementation in Python.
A: Track disc time and low-link. A node is an SCC root if its low-link equals its disc time when it finishes DFS.
Time: O(V + E)  |  Space: O(V)
def count_scc(graph, n):
    disc = [-1] * n
    low = [0] * n
    on_stack = [False] * n
    stack = []
    timer = [0]
    sccs = []
    def dfs(u):
        disc[u] = low[u] = timer[0]
        timer[0] += 1
        stack.append(u)
        on_stack[u] = True
        for v in graph[u]:
            if disc[v] == -1:
                dfs(v)
                low[u] = min(low[u], low[v])
            elif on_stack[v]:
                low[u] = min(low[u], disc[v])
        if low[u] == disc[u]:
            scc = []
            while True:
                v = stack.pop()
                on_stack[v] = False
                scc.append(v)
                if v == u:
                    break
            sccs.append(scc)
    for i in range(n):
        if disc[i] == -1:
            dfs(i)
    return sccs
# graph = {0:[1], 1:[2], 2:[0,3], 3:[4], 4:[5], 5:[3]}
# count_scc(graph, 6) → [[3,5,4],[0,2,1]]

Q: 341. What is the Bipartite Matching (Hungarian Algorithm simplified / Hopcroft-Karp)?
Write the augmenting path approach in Python.
A: Find maximum matching in a bipartite graph. For each left node, try to find an augmenting path via DFS.
Time: O(V·E)  |  Space: O(V)
def max_bipartite_matching(graph, n_left, n_right):
    # graph[u] = list of right nodes u can match to
    match_right = [-1] * n_right
    def dfs(u, visited):
        for v in graph[u]:
            if not visited[v]:
                visited[v] = True
                if match_right[v] == -1 or dfs(match_right[v], visited):
                    match_right[v] = u
                    return True
        return False
    result = 0
    for u in range(n_left):
        visited = [False] * n_right
        if dfs(u, visited):
            result += 1
    return result
# graph = {0:[0,1], 1:[0], 2:[1,2]}
# max_bipartite_matching(graph, 3, 3) → 3

Q: 342. What is the Interval Scheduling Maximization with Weights (DP + Binary Search)?
Write the implementation in Python.
A: Select non-overlapping intervals to maximize total weight. Sort by end time, binary search for the last compatible job.
Time: O(n log n)  |  Space: O(n)
import bisect
def weighted_job_scheduling(jobs):
    # jobs = [(start, end, weight), ...]
    jobs.sort(key=lambda x: x[1])
    n = len(jobs)
    ends = [j[1] for j in jobs]
    dp = [0] * (n + 1)
    for i in range(1, n + 1):
        start_i = jobs[i-1][0]
        weight_i = jobs[i-1][2]
        # Find last job ending <= start_i
        k = bisect.bisect_right(ends, start_i, 0, i-1)
        dp[i] = max(dp[i-1], dp[k] + weight_i)
    return dp[n]
# jobs = [(1,3,50),(2,5,20),(4,6,70),(6,7,60)]
# weighted_job_scheduling(jobs) → 120 (jobs 0 and 2: 50+70)

Q: 343. What is the Detect All Cycles in an Undirected Graph?
Write the implementation in Python.
A: Use DFS with parent tracking. A back edge (neighbor ≠ parent and already visited) indicates a cycle. Track the cycle path via the recursion stack.
Time: O(V + E)  |  Space: O(V)
def find_all_cycles(graph, n):
    color = [0] * n  # 0=white, 1=gray, 2=black
    parent = [-1] * n
    cycles = []
    def dfs(u):
        color[u] = 1
        for v in graph[u]:
            if v == parent[u]:
                continue
            if color[v] == 1:
                # Found cycle, trace back
                cycle = [v]
                curr = u
                while curr != v:
                    cycle.append(curr)
                    curr = parent[curr]
                cycle.append(v)
                cycles.append(cycle)
            elif color[v] == 0:
                parent[v] = u
                dfs(v)
        color[u] = 2
    for i in range(n):
        if color[i] == 0:
            dfs(i)
    return cycles
# graph = {0:[1,2], 1:[0,2], 2:[0,1]}
# find_all_cycles(graph, 3) → one triangle cycle

Q: 344. What is the Rabin-Karp for multiple pattern search?
Write the implementation in Python.
A: Compute rolling hashes for all patterns and store in a set. Slide a window over text and check hash membership.
Time: O(n·k + m) avg (k=num patterns, m=total pattern length)
def multi_pattern_search(text, patterns):
    if not patterns:
        return {}
    base, mod = 31, 10**9 + 9
    results = {p: [] for p in patterns}
    by_len = {}
    for p in patterns:
        by_len.setdefault(len(p), []).append(p)
    def compute_hash(s):
        h = 0
        for c in s:
            h = (h * base + ord(c)) % mod
        return h
    for plen, pats in by_len.items():
        pat_hashes = {}
        for p in pats:
            h = compute_hash(p)
            pat_hashes.setdefault(h, []).append(p)
        # Rolling hash over text
        h = compute_hash(text[:plen])
        power = pow(base, plen - 1, mod)
        for i in range(len(text) - plen + 1):
            if i > 0:
                h = (h - ord(text[i-1]) * power) % mod
                h = (h * base + ord(text[i+plen-1])) % mod
            if h in pat_hashes:
                for p in pat_hashes[h]:
                    if text[i:i+plen] == p:
                        results[p].append(i)
    return results
# multi_pattern_search("abcabcabc", ["abc","cab"])
# → {"abc": [0,3,6], "cab": [2,5]}

Q: 345. What is the Trie-based Autocomplete / Prefix Search?
Write the implementation in Python.
A: Insert words into a Trie, then DFS from the prefix node to collect all words sharing that prefix.
Time: Insert O(L), Search O(P + K) where K=results
class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False
class Autocomplete:
    def __init__(self):
        self.root = TrieNode()
    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True
    def search(self, prefix, limit=10):
        node = self.root
        for c in prefix:
            if c not in node.children:
                return []
            node = node.children[c]
        results = []
        def dfs(node, path):
            if len(results) >= limit:
                return
            if node.is_end:
                results.append(prefix + ''.join(path))
            for c, child in sorted(node.children.items()):
                path.append(c)
                dfs(child, path)
                path.pop()
        dfs(node, [])
        return results
# ac = Autocomplete()
# for w in ["apple","app","ape","april","banana"]:
#     ac.insert(w)
# ac.search("ap") → ["ape","app","apple","april"]

Q: 346. What is the Monotonic Deque for Sliding Window Min/Max?
Write a generic implementation in Python.
A: Maintain a deque of indices in monotonic order. Remove out-of-window and dominated elements. Works for both min and max.
Time: O(n) amortized  |  Space: O(k)
from collections import deque
def sliding_window_min(arr, k):
    dq = deque()  # indices, front = min
    result = []
    for i, x in enumerate(arr):
        while dq and dq[0] < i - k + 1:
            dq.popleft()
        while dq and arr[dq[-1]] >= x:
            dq.pop()
        dq.append(i)
        if i >= k - 1:
            result.append(arr[dq[0]])
    return result
def sliding_window_max(arr, k):
    dq = deque()
    result = []
    for i, x in enumerate(arr):
        while dq and dq[0] < i - k + 1:
            dq.popleft()
        while dq and arr[dq[-1]] <= x:
            dq.pop()
        dq.append(i)
        if i >= k - 1:
            result.append(arr[dq[0]])
    return result
# sliding_window_min([1,3,-1,-3,5,3,6,7], 3)
# → [-1,-3,-3,-3,3,3]

Q: 347. What is the Sparse Table for Range GCD Query?
Write the implementation in Python.
A: Precompute GCD for every power-of-2 length interval. Since GCD is idempotent (overlap-friendly), queries are O(1).
Build: O(n log n)  |  Query: O(1)
from math import gcd, log2
class SparseTableGCD:
    def __init__(self, arr):
        n = len(arr)
        k = int(log2(n)) + 1 if n else 0
        self.table = [[0]*n for _ in range(k)]
        self.table[0] = arr[:]
        for j in range(1, k):
            for i in range(n - (1 << j) + 1):
                self.table[j][i] = gcd(
                    self.table[j-1][i],
                    self.table[j-1][i + (1 << (j-1))]
                )
        self.log = [0] * (n + 1)
        for i in range(2, n + 1):
            self.log[i] = self.log[i//2] + 1
    def query(self, l, r):
        j = self.log[r - l + 1]
        return gcd(self.table[j][l],
                   self.table[j][r - (1 << j) + 1])
# st = SparseTableGCD([12, 6, 9, 3, 15])
# st.query(0, 2) → 3  (gcd(12,6,9))
# st.query(1, 4) → 3  (gcd(6,9,3,15))

Q: 348. What is the Topological Sort with Cycle Detection (DFS)?
Write the implementation in Python.
A: If a gray node (in current DFS path) is revisited, a cycle exists. Otherwise, append nodes in reverse post-order.
Time: O(V + E)  |  Space: O(V)
def topo_sort_with_cycle(graph, n):
    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * n
    order = []
    has_cycle = False
    def dfs(u):
        nonlocal has_cycle
        if has_cycle:
            return
        color[u] = GRAY
        for v in graph.get(u, []):
            if color[v] == GRAY:
                has_cycle = True
                return
            if color[v] == WHITE:
                dfs(v)
        color[u] = BLACK
        order.append(u)
    for u in range(n):
        if color[u] == WHITE:
            dfs(u)
    if has_cycle:
        return None
    return order[::-1]
# graph = {0:[1,2], 1:[3], 2:[3], 3:[]}
# topo_sort_with_cycle(graph, 4) → [0,2,1,3]
# graph_cycle = {0:[1], 1:[2], 2:[0]}
# topo_sort_with_cycle(graph_cycle, 3) → None

Q: 349. What is the Knuth-Morris-Pratt Failure Function (partial match table)?
Explain and write the failure function in Python.
A: The failure function f[i] gives the length of the longest proper prefix of pattern[0..i] that is also a suffix. Used to skip comparisons.
Time: O(m)  |  Space: O(m)
def build_failure(pattern):
    m = len(pattern)
    fail = [0] * m
    j = 0
    for i in range(1, m):
        while j > 0 and pattern[i] != pattern[j]:
            j = fail[j - 1]
        if pattern[i] == pattern[j]:
            j += 1
        fail[i] = j
    return fail
def kmp_search(text, pattern):
    fail = build_failure(pattern)
    j = 0
    matches = []
    for i in range(len(text)):
        while j > 0 and text[i] != pattern[j]:
            j = fail[j - 1]
        if text[i] == pattern[j]:
            j += 1
        if j == len(pattern):
            matches.append(i - j + 1)
            j = fail[j - 1]
    return matches
# build_failure("ABABAC") → [0,0,1,2,3,0]
# kmp_search("ABABABABAC", "ABABAC") → [4]

Q: 350. What is the Cycle Detection in Linked List with entry point (Floyd's extended)?
Write the full implementation in Python.
A: Phase 1: slow/fast meet inside the cycle. Phase 2: reset slow to head, advance both by 1 — they meet at the cycle entry.
Time: O(n)  |  Space: O(1)
def detect_cycle_entry(head):
    slow = fast = head
    # Phase 1: detect cycle
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            break
    else:
        return None  # no cycle
    # Phase 2: find entry
    slow = head
    while slow != fast:
        slow = slow.next
        fast = fast.next
    return slow  # cycle entry node
def cycle_length(entry):
    count = 1
    curr = entry.next
    while curr != entry:
        count += 1
        curr = curr.next
    return count

Q: 351. What is the Linked List Intersection with different lengths?
Write the clean two-pointer solution in Python.
A: Advance two pointers; when one reaches the end, redirect it to the other list's head. They meet at the intersection after equalizing lengths.
Time: O(n + m)  |  Space: O(1)
def get_intersection(headA, headB):
    if not headA or not headB:
        return None
    a, b = headA, headB
    while a != b:
        a = a.next if a else headB
        b = b.next if b else headA
    return a  # None if no intersection
# Why it works:
# Pointer A travels: lenA + lenC + lenB
# Pointer B travels: lenB + lenC + lenA
# Same total → they align at intersection
# (or both reach None if no intersection)

Q: 352. What is the Implement Deque from scratch?
Write the implementation using a doubly-linked list in Python.
A: A deque supports O(1) insert/delete at both ends. Use a doubly-linked list with sentinel head and tail nodes.
All operations: O(1)
class Node:
    def __init__(self, val=0):
        self.val = val
        self.prev = self.next = None
class Deque:
    def __init__(self):
        self.head = Node()  # sentinel
        self.tail = Node()  # sentinel
        self.head.next = self.tail
        self.tail.prev = self.head
        self.size = 0
    def push_front(self, val):
        node = Node(val)
        node.next = self.head.next
        node.prev = self.head
        self.head.next.prev = node
        self.head.next = node
        self.size += 1
    def push_back(self, val):
        node = Node(val)
        node.prev = self.tail.prev
        node.next = self.tail
        self.tail.prev.next = node
        self.tail.prev = node
        self.size += 1
    def pop_front(self):
        if self.size == 0: return None
        node = self.head.next
        self.head.next = node.next
        node.next.prev = self.head
        self.size -= 1
        return node.val
    def pop_back(self):
        if self.size == 0: return None
        node = self.tail.prev
        self.tail.prev = node.prev
        node.prev.next = self.tail
        self.size -= 1
        return node.val

Q: 353. What is the Ordered Set / SortedList in Python?
Write a usage example and a simplified implementation.
A: Python's sortedcontainers.SortedList provides O(log n) insert/delete/search. For competitive programming, you can use a BIT-backed order-statistic structure.
Insert/Delete/Search: O(log n)
# Using sortedcontainers (pip install sortedcontainers)
from sortedcontainers import SortedList
sl = SortedList([5, 1, 3])
sl.add(2)          # [1, 2, 3, 5]
sl.remove(3)       # [1, 2, 5]
sl.bisect_left(2)  # 1 (index of 2)
sl[0]              # 1 (min)
sl[-1]             # 5 (max)
# BIT-backed order statistic (values in [0, MAXVAL])
class OrderStatistic:
    def __init__(self, maxval):
        self.n = maxval + 1
        self.bit = [0] * (self.n + 1)
    def update(self, i, delta):
        i += 1
        while i <= self.n:
            self.bit[i] += delta
            i += i & (-i)
    def query(self, i):
        i += 1
        s = 0
        while i > 0:
            s += self.bit[i]
            i -= i & (-i)
        return s
    def kth(self, k):
        pos = 0
        bitlen = self.n.bit_length()
        for i in range(bitlen, -1, -1):
            npos = pos + (1 << i)
            if npos <= self.n and self.bit[npos] < k:
                k -= self.bit[npos]
                pos = npos
        return pos

Q: 354. What is the Sweep Line for Interval Union Length?
Write the implementation in Python.
A: Sort interval start (+1) and end (-1) events. Track active count; sum lengths where count > 0.
Time: O(n log n)  |  Space: O(n)
def union_length(intervals):
    events = []
    for start, end in intervals:
        events.append((start, 1))   # interval opens
        events.append((end, -1))    # interval closes
    events.sort()
    total_length = 0
    active = 0
    prev_x = None
    for x, typ in events:
        if active > 0 and prev_x is not None:
            total_length += x - prev_x
        active += typ
        prev_x = x
    return total_length
# Total covered length
# union_length([(1,5),(2,7),(9,12)]) → 9
# Union: [1,7] ∪ [9,12] = 6 + 3 = 9

Q: 355. What is the Euler's Totient Function (Phi)?
Write the implementation in Python.
A: φ(n) counts integers in [1,n] that are coprime to n. For prime p: φ(p)=p-1. Formula: n · ∏(1 - 1/p) for each prime factor p of n.
Time: O(√n) single  |  O(n log log n) sieve
# Single value
def euler_totient(n):
    result = n
    p = 2
    while p * p <= n:
        if n % p == 0:
            while n % p == 0:
                n //= p
            result -= result // p
        p += 1
    if n > 1:
        result -= result // n
    return result
# Sieve for all values up to N
def totient_sieve(N):
    phi = list(range(N + 1))
    for i in range(2, N + 1):
        if phi[i] == i:  # i is prime
            for j in range(i, N + 1, i):
                phi[j] -= phi[j] // i
    return phi
# euler_totient(12) → 4 (1,5,7,11 coprime to 12)
# totient_sieve(10) → [0,1,1,2,2,4,2,6,4,6,4]

Q: 356. What is the Prime Factorization using smallest prime factor sieve?
Write the implementation in Python.
A: Precompute the smallest prime factor (SPF) for every number via sieve. Then factorize any number by dividing by its SPF repeatedly.
Sieve: O(n log log n)  |  Factorize: O(log n)
def spf_sieve(N):
    spf = list(range(N + 1))
    for i in range(2, int(N**0.5) + 1):
        if spf[i] == i:  # i is prime
            for j in range(i*i, N + 1, i):
                if spf[j] == j:
                    spf[j] = i
    return spf
def factorize(n, spf):
    factors = {}
    while n > 1:
        p = spf[n]
        while n % p == 0:
            factors[p] = factors.get(p, 0) + 1
            n //= p
    return factors
# spf = spf_sieve(100)
# factorize(60, spf) → {2: 2, 3: 1, 5: 1}
# factorize(84, spf) → {2: 2, 3: 1, 7: 1}

Q: 357. What is the Maximum Bipartite Matching count (Koenig's Theorem)?
Write the implementation showing Koenig's Theorem in Python.
A: Koenig's: in a bipartite graph, max matching = min vertex cover. Find matching, then use alternating BFS to identify the cover.
Time: O(V·E)
def min_vertex_cover(graph, n_left, n_right):
    match_l = [-1] * n_left
    match_r = [-1] * n_right
    def dfs(u, vis):
        for v in graph[u]:
            if not vis[v]:
                vis[v] = True
                if match_r[v] == -1 or dfs(match_r[v], vis):
                    match_l[u] = v
                    match_r[v] = u
                    return True
        return False
    matching = 0
    for u in range(n_left):
        vis = [False] * n_right
        if dfs(u, vis):
            matching += 1
    # Koenig's: find min vertex cover
    from collections import deque
    unmatched_l = {u for u in range(n_left) if match_l[u] == -1}
    visited_l = set()
    visited_r = set()
    queue = deque(unmatched_l)
    visited_l = set(unmatched_l)
    while queue:
        u = queue.popleft()
        for v in graph[u]:
            if v not in visited_r:
                visited_r.add(v)
                if match_r[v] != -1 and match_r[v] not in visited_l:
                    visited_l.add(match_r[v])
                    queue.append(match_r[v])
    # Cover = left NOT visited + right visited
    cover_l = [u for u in range(n_left) if u not in visited_l]
    cover_r = [v for v in range(n_right) if v in visited_r]
    return matching, cover_l, cover_r

Q: 358. What is the Count Subarrays with Given XOR?
Write the implementation in Python.
A: Use prefix XOR and a hash map. If prefix_xor[j] ^ prefix_xor[i] = target, then prefix_xor[i] = prefix_xor[j] ^ target.
Time: O(n)  |  Space: O(n)
def count_subarrays_xor(arr, target):
    prefix_xor = 0
    count = 0
    freq = {0: 1}
    for x in arr:
        prefix_xor ^= x
        want = prefix_xor ^ target
        count += freq.get(want, 0)
        freq[prefix_xor] = freq.get(prefix_xor, 0) + 1
    return count
# count_subarrays_xor([4, 2, 2, 6, 4], 6) → 4
# Subarrays: [4,2], [4,2,2,6,4], [2,2,6], [6]

Q: 359. What is the Minimum Spanning Arborescence (Edmonds' / Chu-Liu)?
Write the implementation in Python.
A: Find the minimum weight rooted spanning tree in a directed graph. Repeatedly contract cycles and adjust weights.
Time: O(VE)
def min_arborescence(n, edges, root):
    # edges = [(u, v, w), ...]
    INF = float('inf')
    res = 0
    while True:
        # Find min incoming edge for each node
        min_in = [INF] * n
        best_from = [-1] * n
        for u, v, w in edges:
            if u != v and v != root and w < min_in[v]:
                min_in[v] = w
                best_from[v] = u
        for i in range(n):
            if i != root and min_in[i] == INF:
                return INF  # unreachable
        # Detect cycles in chosen edges
        comp = [-1] * n
        comp[root] = root
        num_comp = 0
        visited = [-1] * n
        for i in range(n):
            if i == root:
                continue
            res += min_in[i]
            u = i
            while visited[u] == -1 and comp[u] == -1:
                visited[u] = i
                u = best_from[u]
            if comp[u] == -1 and u != root:
                # Found cycle, label it
                v = u
                while True:
                    comp[v] = num_comp
                    v = best_from[v]
                    if v == u: break
                num_comp += 1
        if num_comp == 0:
            break
        for i in range(n):
            if comp[i] == -1:
                comp[i] = num_comp
                num_comp += 1
        # Contract and adjust edges
        new_edges = []
        for u, v, w in edges:
            nu, nv = comp[u], comp[v]
            if nu != nv:
                new_edges.append((nu, nv, w - min_in[v]))
        edges = new_edges
        root = comp[root]
        n = num_comp
    return res

Q: 360. What is the Maximum Flow Min Cost (MCMF) using SPFA?
Write the implementation in Python.
A: Augment flow along the shortest cost path (found via SPFA/Bellman-Ford). Repeat until no augmenting path exists.
Time: O(V·E·F) where F = max flow
from collections import deque
def mcmf(n, edges, source, sink):
    # edges = [(u, v, cap, cost), ...]
    graph = [[] for _ in range(n)]
    def add_edge(u, v, cap, cost):
        graph[u].append([v, cap, cost, len(graph[v])])
        graph[v].append([u, 0, -cost, len(graph[u])-1])
    for u, v, cap, cost in edges:
        add_edge(u, v, cap, cost)
    total_flow = total_cost = 0
    while True:
        dist = [float('inf')] * n
        dist[source] = 0
        in_queue = [False] * n
        prev_v = [-1] * n
        prev_e = [-1] * n
        q = deque([source])
        in_queue[source] = True
        while q:
            u = q.popleft()
            in_queue[u] = False
            for i, (v, cap, cost, _) in enumerate(graph[u]):
                if cap > 0 and dist[u] + cost < dist[v]:
                    dist[v] = dist[u] + cost
                    prev_v[v] = u
                    prev_e[v] = i
                    if not in_queue[v]:
                        q.append(v)
                        in_queue[v] = True
        if dist[sink] == float('inf'):
            break
        # Find bottleneck
        flow = float('inf')
        v = sink
        while v != source:
            flow = min(flow, graph[prev_v[v]][prev_e[v]][1])
            v = prev_v[v]
        # Augment
        v = sink
        while v != source:
            e = graph[prev_v[v]][prev_e[v]]
            e[1] -= flow
            graph[v][e[3]][1] += flow
            v = prev_v[v]
        total_flow += flow
        total_cost += flow * dist[sink]
    return total_flow, total_cost

Q: 361. What is the Counting Sort for negative numbers?
Write the implementation in Python.
A: Shift all values by min_val so indices are non-negative. Apply standard counting sort on shifted values, then shift back.
Time: O(n + k)  |  Space: O(k)
def counting_sort_neg(arr):
    if not arr:
        return arr
    min_val = min(arr)
    max_val = max(arr)
    k = max_val - min_val + 1
    count = [0] * k
    for x in arr:
        count[x - min_val] += 1
    result = []
    for i in range(k):
        result.extend([i + min_val] * count[i])
    return result
# counting_sort_neg([-5, -3, 0, 2, -1, 4, -3])
# → [-5, -3, -3, -1, 0, 2, 4]

Q: 362. What is the Double-Ended BFS (Meet in the Middle)?
Write the implementation in Python.
A: Expand from both source and target simultaneously. Switch to expanding the smaller frontier. Halves the search space exponentially.
Time: O(b^(d/2)) vs O(b^d) for normal BFS
def bidirectional_bfs(graph, start, end):
    if start == end:
        return 0
    front_s = {start}
    front_e = {end}
    visited_s = {start: 0}
    visited_e = {end: 0}
    level = 0
    while front_s and front_e:
        level += 1
        # Expand smaller frontier
        if len(front_s) > len(front_e):
            front_s, front_e = front_e, front_s
            visited_s, visited_e = visited_e, visited_s
        next_front = set()
        for node in front_s:
            for neighbor in graph.get(node, []):
                if neighbor in visited_e:
                    return visited_s[node] + 1 + visited_e[neighbor]
                if neighbor not in visited_s:
                    visited_s[neighbor] = level
                    next_front.add(neighbor)
        front_s = next_front
    return -1  # no path
# graph = {1:[2,3], 2:[1,4], 3:[1,4], 4:[2,3,5], 5:[4]}
# bidirectional_bfs(graph, 1, 5) → 3

Q: 363. What is the Interval Map / MyCalendar problem?
Write the implementation in Python.
A: Track booked intervals using a sorted list. For each new booking, binary search to check overlap with neighbors.
Book: O(log n)  |  Space: O(n)
import bisect
class MyCalendar:
    def __init__(self):
        self.starts = []
        self.ends = []
    def book(self, start, end):
        i = bisect.bisect_right(self.starts, start)
        if i > 0 and self.ends[i-1] > start:
            return False
        if i < len(self.starts) and self.starts[i] < end:
            return False
        self.starts.insert(i, start)
        self.ends.insert(i, end)
        return True
# cal = MyCalendar()
# cal.book(10, 20) → True
# cal.book(15, 25) → False (overlap)
# cal.book(20, 30) → True

Q: 364. What is the Longest Substring with At Most K Distinct Characters?
Write the sliding window solution in Python.
A: Maintain a window with a hash map of char counts. Shrink the left side when distinct chars exceed k.
Time: O(n)  |  Space: O(k)
def longest_k_distinct(s, k):
    freq = {}
    left = 0
    max_len = 0
    for right in range(len(s)):
        freq[s[right]] = freq.get(s[right], 0) + 1
        while len(freq) > k:
            freq[s[left]] -= 1
            if freq[s[left]] == 0:
                del freq[s[left]]
            left += 1
        max_len = max(max_len, right - left + 1)
    return max_len
# longest_k_distinct("eceba", 2) → 3 ("ece")
# longest_k_distinct("aa", 1) → 2

Q: 365. What is the Smallest Window Containing All Characters of Another String?
Write the implementation in Python.
A: Sliding window with two frequency maps. Expand right to cover all chars, then shrink left to minimize the window.
Time: O(n + m)  |  Space: O(m)
def smallest_window(s, t):
    from collections import Counter
    need = Counter(t)
    have = {}
    formed = 0
    required = len(need)
    best = (float('inf'), 0, 0)
    left = 0
    for right in range(len(s)):
        c = s[right]
        have[c] = have.get(c, 0) + 1
        if c in need and have[c] == need[c]:
            formed += 1
        while formed == required:
            size = right - left + 1
            if size < best[0]:
                best = (size, left, right)
            lc = s[left]
            have[lc] -= 1
            if lc in need and have[lc] < need[lc]:
                formed -= 1
            left += 1
    return "" if best[0] == float('inf') else s[best[1]:best[2]+1]
# smallest_window("ADOBECODEBANC", "ABC") → "BANC"

Q: 366. What is the Subsets with Duplicates (backtracking)?
Write the implementation in Python.
A: Sort the array first. When iterating, skip duplicate elements at the same recursion level to avoid duplicate subsets.
Time: O(n · 2^n)  |  Space: O(n)
def subsets_with_dup(nums):
    nums.sort()
    result = []
    def backtrack(start, path):
        result.append(path[:])
        for i in range(start, len(nums)):
            if i > start and nums[i] == nums[i-1]:
                continue
            path.append(nums[i])
            backtrack(i + 1, path)
            path.pop()
    backtrack(0, [])
    return result
# subsets_with_dup([1, 2, 2])
# → [[], [1], [1,2], [1,2,2], [2], [2,2]]

Q: 367. What is the Permutations with Duplicates (backtracking)?
Write the implementation in Python.
A: Sort and use a visited array. Skip an element if it equals the previous one and the previous one hasn't been used in this branch.
Time: O(n · n!)
def permute_unique(nums):
    nums.sort()
    result = []
    used = [False] * len(nums)
    def backtrack(path):
        if len(path) == len(nums):
            result.append(path[:])
            return
        for i in range(len(nums)):
            if used[i]:
                continue
            if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
                continue
            used[i] = True
            path.append(nums[i])
            backtrack(path)
            path.pop()
            used[i] = False
    backtrack([])
    return result
# permute_unique([1, 1, 2])
# → [[1,1,2],[1,2,1],[2,1,1]]

Q: 368. What is the Letter Combinations of a Phone Number (backtracking)?
Write the implementation in Python.
A: Map each digit to its letters. Use backtracking to build all combinations by picking one letter per digit.
Time: O(4^n)  |  Space: O(n)
def letter_combinations(digits):
    if not digits:
        return []
    phone = {'2':'abc','3':'def','4':'ghi','5':'jkl',
             '6':'mno','7':'pqrs','8':'tuv','9':'wxyz'}
    result = []
    def backtrack(idx, path):
        if idx == len(digits):
            result.append(''.join(path))
            return
        for c in phone[digits[idx]]:
            path.append(c)
            backtrack(idx + 1, path)
            path.pop()
    backtrack(0, [])
    return result
# letter_combinations("23")
# → ["ad","ae","af","bd","be","bf","cd","ce","cf"]

Q: 369. What is the Restore IP Addresses problem (backtracking)?
Write the implementation in Python.
A: Place 3 dots to split a string into 4 valid octets (0-255, no leading zeros). Use backtracking with pruning on remaining length.
Time: O(1) bounded  |  Space: O(1)
def restore_ip(s):
    result = []
    def backtrack(start, parts):
        if len(parts) == 4:
            if start == len(s):
                result.append('.'.join(parts))
            return
        remaining = len(s) - start
        needed = 4 - len(parts)
        if remaining < needed or remaining > needed * 3:
            return
        for end in range(start + 1, min(start + 4, len(s) + 1)):
            segment = s[start:end]
            if (segment[0] == '0' and len(segment) > 1) or int(segment) > 255:
                continue
            backtrack(end, parts + [segment])
    backtrack(0, [])
    return result
# restore_ip("25525511135")
# → ["255.255.11.135","255.255.111.35"]

Q: 370. What is the Generate Parentheses problem (backtracking)?
Write the implementation in Python.
A: Track open and close counts. Add open if count < n. Add close if close_count < open_count.
Time: O(4^n / √n) Catalan number
def generate_parentheses(n):
    result = []
    def backtrack(path, open_ct, close_ct):
        if len(path) == 2 * n:
            result.append(''.join(path))
            return
        if open_ct < n:
            path.append('(')
            backtrack(path, open_ct + 1, close_ct)
            path.pop()
        if close_ct < open_ct:
            path.append(')')
            backtrack(path, open_ct, close_ct + 1)
            path.pop()
    backtrack([], 0, 0)
    return result
# generate_parentheses(3)
# → ["((()))","(()())","(())()","()(())","()()()"]

Q: 371. What is the Spiral Order Matrix Traversal?
Write the implementation in Python.
A: Maintain four boundaries: top, bottom, left, right. Traverse right→down→left→up, shrinking boundaries after each pass.
Time: O(m·n)  |  Space: O(1) (excl. output)
def spiral_order(matrix):
    if not matrix:
        return []
    result = []
    top, bottom = 0, len(matrix) - 1
    left, right = 0, len(matrix[0]) - 1
    while top <= bottom and left <= right:
        for j in range(left, right + 1):
            result.append(matrix[top][j])
        top += 1
        for i in range(top, bottom + 1):
            result.append(matrix[i][right])
        right -= 1
        if top <= bottom:
            for j in range(right, left - 1, -1):
                result.append(matrix[bottom][j])
            bottom -= 1
        if left <= right:
            for i in range(bottom, top - 1, -1):
                result.append(matrix[i][left])
            left += 1
    return result
# spiral_order([[1,2,3],[4,5,6],[7,8,9]])
# → [1,2,3,6,9,8,7,4,5]

Q: 372. What is the Generate Spiral Matrix (fill n×n with 1..n²)?
Write the implementation in Python.
A: Same boundary approach but fill values 1..n² instead of reading. Increment a counter at each cell.
Time: O(n²)  |  Space: O(n²)
def generate_spiral(n):
    matrix = [[0] * n for _ in range(n)]
    num = 1
    top, bottom, left, right = 0, n-1, 0, n-1
    while top <= bottom and left <= right:
        for j in range(left, right + 1):
            matrix[top][j] = num; num += 1
        top += 1
        for i in range(top, bottom + 1):
            matrix[i][right] = num; num += 1
        right -= 1
        if top <= bottom:
            for j in range(right, left - 1, -1):
                matrix[bottom][j] = num; num += 1
            bottom -= 1
        if left <= right:
            for i in range(bottom, top - 1, -1):
                matrix[i][left] = num; num += 1
            left += 1
    return matrix
# generate_spiral(3)
# → [[1,2,3],[8,9,4],[7,6,5]]

Q: 373. What is the Search in Rotated Sorted Array problem?
Write the implementation in Python.
A: Modified binary search: one half is always sorted. Check if target lies in the sorted half, otherwise search the other half.
Time: O(log n)  |  Space: O(1)
def search_rotated(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if nums[mid] == target:
            return mid
        if nums[lo] <= nums[mid]:  # left half sorted
            if nums[lo] <= target < nums[mid]:
                hi = mid - 1
            else:
                lo = mid + 1
        else:  # right half sorted
            if nums[mid] < target <= nums[hi]:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1
# search_rotated([4,5,6,7,0,1,2], 0) → 4
# search_rotated([4,5,6,7,0,1,2], 3) → -1

Q: 374. What is the Find Minimum in Rotated Sorted Array?
Write the implementation in Python.
A: Binary search: if mid > right, minimum is in the right half; otherwise it's in the left half (including mid).
Time: O(log n)  |  Space: O(1)
def find_min_rotated(nums):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        mid = (lo + hi) // 2
        if nums[mid] > nums[hi]:
            lo = mid + 1
        else:
            hi = mid
    return nums[lo]
# With duplicates
def find_min_rotated_dup(nums):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        mid = (lo + hi) // 2
        if nums[mid] > nums[hi]:
            lo = mid + 1
        elif nums[mid] < nums[hi]:
            hi = mid
        else:
            hi -= 1  # can't decide, shrink
    return nums[lo]
# find_min_rotated([3,4,5,1,2]) → 1
# find_min_rotated_dup([2,2,2,0,1]) → 0

Q: 375. What is the Peak Element problem (Binary Search)?
Write the implementation in Python.
A: If mid < mid+1, a peak must exist on the right; otherwise on the left (or mid itself). Works because boundaries are -∞.
Time: O(log n)  |  Space: O(1)
def find_peak(nums):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        mid = (lo + hi) // 2
        if nums[mid] < nums[mid + 1]:
            lo = mid + 1
        else:
            hi = mid
    return lo
# find_peak([1,2,3,1]) → 2
# find_peak([1,2,1,3,5,6,4]) → 1 or 5

Q: 376. What is the Search a 2D Matrix (binary search)?
Write the implementation in Python.
A: Treat the m×n matrix as a sorted 1D array. Map index to row/col: row=idx//n, col=idx%n.
Time: O(log(m·n))  |  Space: O(1)
def search_matrix(matrix, target):
    if not matrix:
        return False
    m, n = len(matrix), len(matrix[0])
    lo, hi = 0, m * n - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        val = matrix[mid // n][mid % n]
        if val == target:
            return True
        elif val < target:
            lo = mid + 1
        else:
            hi = mid - 1
    return False
# Variant: rows sorted, first elem > last of prev row
# search_matrix([[1,3,5],[10,11,16],[23,30,34]], 3) → True
# Variant II: rows and cols sorted independently
def search_matrix_ii(matrix, target):
    r, c = 0, len(matrix[0]) - 1
    while r < len(matrix) and c >= 0:
        if matrix[r][c] == target:
            return True
        elif matrix[r][c] > target:
            c -= 1
        else:
            r += 1
    return False

Q: 377. What is the Koko Eating Bananas problem (Binary Search on Answer)?
Write the implementation in Python.
A: Binary search on eating speed k. For each k, check if Koko can finish all piles in h hours. Minimum k where she can finish.
Time: O(n log M) where M = max pile
import math
def min_eating_speed(piles, h):
    def can_finish(speed):
        return sum(math.ceil(p / speed) for p in piles) <= h
    lo, hi = 1, max(piles)
    while lo < hi:
        mid = (lo + hi) // 2
        if can_finish(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo
# min_eating_speed([3, 6, 7, 11], 8) → 4
# Speed 4: ceil(3/4)+ceil(6/4)+ceil(7/4)+ceil(11/4)
#        = 1 + 2 + 2 + 3 = 8 ≤ 8 ✓

Q: 378. What is the Split Array Largest Sum (Binary Search on Answer)?
Write the implementation in Python.
A: Binary search on the maximum subarray sum. Greedily check if we can split into ≤ k subarrays each with sum ≤ mid.
Time: O(n log S) where S = sum of array
def split_array(nums, k):
    def can_split(max_sum):
        count = 1
        curr = 0
        for x in nums:
            if curr + x > max_sum:
                count += 1
                curr = x
                if count > k:
                    return False
            else:
                curr += x
        return True
    lo, hi = max(nums), sum(nums)
    while lo < hi:
        mid = (lo + hi) // 2
        if can_split(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo
# split_array([7, 2, 5, 10, 8], 2) → 18
# Split: [7,2,5] and [10,8] → max(14,18) = 18

Q: 379. What is the Largest Number formed from an array?
Write the implementation in Python.
A: Sort numbers by a custom comparator: a+b vs b+a as strings. Concatenate in that order for the largest possible number.
Time: O(n log n · k) where k = avg digit length
from functools import cmp_to_key
def largest_number(nums):
    strs = [str(x) for x in nums]
    strs.sort(key=cmp_to_key(
        lambda a, b: -1 if a+b > b+a else (1 if a+b < b+a else 0)
    ))
    result = ''.join(strs)
    return '0' if result[0] == '0' else result
# largest_number([3, 30, 34, 5, 9]) → "9534330"
# largest_number([10, 2]) → "210"
# largest_number([0, 0]) → "0"

Q: 380. What is the Majority Element in a Stream (Misra-Gries)?
Write the implementation in Python.
A: Generalization of Boyer-Moore: find elements appearing more than n/k times. Maintain k-1 candidates with counts.
Time: O(n·k)  |  Space: O(k)
def misra_gries(stream, k):
    candidates = {}  # val → count
    for x in stream:
        if x in candidates:
            candidates[x] += 1
        elif len(candidates) < k - 1:
            candidates[x] = 1
        else:
            to_remove = []
            for key in candidates:
                candidates[key] -= 1
                if candidates[key] == 0:
                    to_remove.append(key)
            for key in to_remove:
                del candidates[key]
    # Verify candidates (second pass)
    counts = {c: 0 for c in candidates}
    for x in stream:
        if x in counts:
            counts[x] += 1
    n = len(stream)
    return [c for c, cnt in counts.items() if cnt > n // k]
# misra_gries([1,1,1,2,2,3,3,3,3], 4)
# Elements appearing > 9/4 = 2 times → [1, 3]

Q: 381. What is the Happy Number problem?
Write the implementation in Python.
A: Repeatedly sum the squares of digits. Use Floyd's cycle detection — if it reaches 1 it's happy; if it cycles, it's not.
Time: O(log n)  |  Space: O(1)
def is_happy(n):
    def next_num(x):
        total = 0
        while x:
            x, d = divmod(x, 10)
            total += d * d
        return total
    slow = n
    fast = next_num(n)
    while fast != 1 and slow != fast:
        slow = next_num(slow)
        fast = next_num(next_num(fast))
    return fast == 1
# is_happy(19) → True
# 19→82→68→100→1 ✓
# is_happy(2) → False (enters cycle)

Q: 382. What is the Count Primes less than N (Sieve optimized)?
Write the implementation with optimizations in Python.
A: Standard Sieve of Eratosthenes with bit-level optimizations: skip evens, start marking from p², only check up to √n.
Time: O(n log log n)  |  Space: O(n)
def count_primes(n):
    if n < 3:
        return 0
    is_prime = bytearray(b'\x01') * n
    is_prime[0] = is_prime[1] = 0
    for i in range(2, int(n**0.5) + 1):
        if is_prime[i]:
            is_prime[i*i::i] = bytearray(len(is_prime[i*i::i]))
    return sum(is_prime)
# Segmented sieve for large ranges
def segmented_sieve(lo, hi):
    import math
    limit = int(math.sqrt(hi)) + 1
    primes = []
    sieve = [True] * (limit + 1)
    for i in range(2, limit + 1):
        if sieve[i]:
            primes.append(i)
            for j in range(i*i, limit+1, i):
                sieve[j] = False
    is_prime = [True] * (hi - lo + 1)
    for p in primes:
        start = max(p*p, ((lo + p - 1) // p) * p)
        for j in range(start, hi+1, p):
            is_prime[j - lo] = False
    if lo <= 1:
        is_prime[1 - lo] = False
    return [i + lo for i in range(hi - lo + 1) if is_prime[i]]
# count_primes(100) → 25

Q: 383. What is the LRU Cache with TTL (Time-To-Live)?
Write the implementation in Python.
A: Extend standard LRU with an expiry timestamp. On get/put, check if the entry is expired and evict if so.
Get/Put: O(1)
import time
from collections import OrderedDict
class LRUCacheTTL:
    def __init__(self, capacity, ttl_seconds):
        self.cap = capacity
        self.ttl = ttl_seconds
        self.cache = OrderedDict()  # key → (value, expiry)
    def _is_expired(self, key):
        if key in self.cache:
            _, expiry = self.cache[key]
            if time.time() > expiry:
                del self.cache[key]
                return True
        return False
    def get(self, key):
        if key not in self.cache or self._is_expired(key):
            return -1
        self.cache.move_to_end(key)
        return self.cache[key][0]
    def put(self, key, value):
        if key in self.cache:
            del self.cache[key]
        elif len(self.cache) >= self.cap:
            self.cache.popitem(last=False)
        self.cache[key] = (value, time.time() + self.ttl)
# cache = LRUCacheTTL(2, 5)  # cap=2, ttl=5s
# cache.put(1, "a")
# cache.get(1) → "a"
# After 5 seconds → cache.get(1) → -1

Q: 384. What is the Consistent Hashing algorithm?
Write the implementation in Python.
A: Map both servers and keys to a ring (0 to 2^32). A key is assigned to the next server clockwise. Adding/removing a server only remaps ~1/n keys.
Lookup: O(log n)  |  Add/Remove: O(k log n)
import bisect
import hashlib
class ConsistentHash:
    def __init__(self, replicas=150):
        self.replicas = replicas
        self.ring = []       # sorted hash positions
        self.node_map = {}   # hash → node name
    def _hash(self, key):
        return int(hashlib.md5(key.encode()).hexdigest(), 16)
    def add_node(self, node):
        for i in range(self.replicas):
            h = self._hash(f"{node}:{i}")
            bisect.insort(self.ring, h)
            self.node_map[h] = node
    def remove_node(self, node):
        for i in range(self.replicas):
            h = self._hash(f"{node}:{i}")
            self.ring.remove(h)
            del self.node_map[h]
    def get_node(self, key):
        if not self.ring:
            return None
        h = self._hash(key)
        idx = bisect.bisect_right(self.ring, h) % len(self.ring)
        return self.node_map[self.ring[idx]]
# ch = ConsistentHash(replicas=3)
# ch.add_node("server1")
# ch.add_node("server2")
# ch.get_node("my_key") → "server1" or "server2"

Q: 385. What is the Trie-based IP Routing (Longest Prefix Match)?
Write the implementation in Python.
A: Store IP prefixes in a binary trie. For each lookup, walk the trie bit-by-bit and return the deepest matching prefix.
Insert/Lookup: O(W) where W=32 for IPv4
class IPTrie:
    def __init__(self):
        self.children = [None, None]
        self.value = None  # next-hop info
    def insert(self, ip_int, prefix_len, value):
        node = self
        for i in range(31, 31 - prefix_len, -1):
            bit = (ip_int >> i) & 1
            if node.children[bit] is None:
                node.children[bit] = IPTrie()
            node = node.children[bit]
        node.value = value
    def lookup(self, ip_int):
        node = self
        best = None
        for i in range(31, -1, -1):
            bit = (ip_int >> i) & 1
            if node.children[bit] is None:
                break
            node = node.children[bit]
            if node.value is not None:
                best = node.value
        return best
def ip_to_int(ip):
    parts = list(map(int, ip.split('.')))
    return (parts[0]<<24)|(parts[1]<<16)|(parts[2]<<8)|parts[3]
# trie = IPTrie()
# trie.insert(ip_to_int("192.168.1.0"), 24, "LAN")
# trie.insert(ip_to_int("192.168.0.0"), 16, "WAN")
# trie.lookup(ip_to_int("192.168.1.5")) → "LAN"

Q: 386. What is the Reservoir Sampling for K items?
Write the implementation in Python.
A: Generalization of reservoir sampling: keep k items. For the i-th element (i≥k), replace a random item with probability k/i.
Time: O(n)  |  Space: O(k)
import random
def reservoir_sample_k(stream, k):
    reservoir = []
    for i, item in enumerate(stream):
        if i < k:
            reservoir.append(item)
        else:
            j = random.randint(0, i)
            if j < k:
                reservoir[j] = item
    return reservoir
# Weighted reservoir sampling (A-ES algorithm)
def weighted_reservoir(stream, k):
    import heapq
    import math
    heap = []  # min-heap of (key, item)
    for item, weight in stream:
        key = random.random() ** (1.0 / weight)
        if len(heap) < k:
            heapq.heappush(heap, (key, item))
        elif key > heap[0][0]:
            heapq.heapreplace(heap, (key, item))
    return [item for _, item in heap]
# reservoir_sample_k(range(100), 5) → 5 random items

Q: 387. What is the Count Distinct Elements in a Sliding Window?
Write the implementation in Python.
A: Maintain a hash map of frequencies. Add the right element, remove the left. Track count of keys with freq > 0.
Time: O(n)  |  Space: O(k)
def count_distinct_window(arr, k):
    freq = {}
    result = []
    for i in range(len(arr)):
        # Add right element
        freq[arr[i]] = freq.get(arr[i], 0) + 1
        # Remove left element when window exceeds k
        if i >= k:
            left = arr[i - k]
            freq[left] -= 1
            if freq[left] == 0:
                del freq[left]
        # Record result when window is full
        if i >= k - 1:
            result.append(len(freq))
    return result
# count_distinct_window([1,2,1,3,4,2,3], 4)
# → [3, 4, 4, 3]
# Windows: [1,2,1,3]→3, [2,1,3,4]→4, [1,3,4,2]→4, [3,4,2,3]→3

Q: 388. What is the Nth Fibonacci using Matrix Exponentiation?
Write the implementation in Python.
A: Compute [[1,1],[1,0]]^n to get F(n) in O(log n). Generalizes to any linear recurrence.
Time: O(log n)  |  Space: O(1)
def mat_mult(A, B, mod=10**9+7):
    n = len(A)
    C = [[0]*n for _ in range(n)]
    for i in range(n):
        for j in range(n):
            for k in range(n):
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod
    return C
def mat_pow(M, p, mod=10**9+7):
    n = len(M)
    result = [[int(i==j) for j in range(n)] for i in range(n)]
    while p > 0:
        if p % 2 == 1:
            result = mat_mult(result, M, mod)
        M = mat_mult(M, M, mod)
        p //= 2
    return result
def fib(n, mod=10**9+7):
    if n <= 1:
        return n
    M = [[1, 1], [1, 0]]
    result = mat_pow(M, n - 1, mod)
    return result[0][0]
# fib(10) → 55
# fib(10**18, 10**9+7) → computed in O(log n)

Q: 389. What is the Number of Ways to Decode a Message (follow-up with wildcards)?
Write the implementation in Python.
A: Extend Decode Ways: '*' can be 1-9. Track dp for single-digit and two-digit decodings, multiplying by the wildcard options.
Time: O(n)  |  Space: O(1)
def num_decodings_wildcard(s):
    MOD = 10**9 + 7
    if not s or s[0] == '0':
        return 0
    prev2, prev1 = 1, 9 if s[0] == '*' else 1
    for i in range(1, len(s)):
        curr = 0
        # Single digit
        if s[i] == '*':
            curr = 9 * prev1
        elif s[i] != '0':
            curr = prev1
        # Two digits
        if s[i-1] == '*' and s[i] == '*':
            curr += 15 * prev2  # 11-19 (9) + 21-26 (6)
        elif s[i-1] == '*':
            d = int(s[i])
            curr += (2 if d <= 6 else 1) * prev2
        elif s[i] == '*':
            if s[i-1] == '1':
                curr += 9 * prev2
            elif s[i-1] == '2':
                curr += 6 * prev2
        else:
            two = int(s[i-1:i+1])
            if 10 <= two <= 26:
                curr += prev2
        curr %= MOD
        prev2, prev1 = prev1, curr
    return prev1
# num_decodings_wildcard("*") → 9
# num_decodings_wildcard("1*") → 18

Q: 390. What is the Minimum Deletions to Make String Balanced (only 'a' and 'b')?
Write the implementation in Python.
A: Track count of 'b' seen so far. At each 'a', either delete it or delete all previous 'b's. Take the minimum of both choices.
Time: O(n)  |  Space: O(1)
def min_deletions_balanced(s):
    b_count = 0
    deletions = 0
    for c in s:
        if c == 'b':
            b_count += 1
        else:  # c == 'a'
            deletions = min(deletions + 1, b_count)
    return deletions
# min_deletions_balanced("aababbab") → 2
# Delete s[2] and s[6] → "aaabbb"
# min_deletions_balanced("bbaaaaabb") → 2

Q: 391. What is the Detect Squares problem (count axis-aligned squares)?
Write the implementation in Python.
A: Store points in a counter by coordinate. For each pair of points forming a diagonal, check if the other two corners exist.
Add: O(1)  |  Count: O(n)
from collections import defaultdict
class DetectSquares:
    def __init__(self):
        self.points = defaultdict(int)  # (x,y) → count
        self.x_map = defaultdict(list)  # x → [y values]
    def add(self, point):
        x, y = point
        self.points[(x, y)] += 1
        self.x_map[x].append(y)
    def count(self, point):
        px, py = point
        total = 0
        for y in self.x_map[px]:
            if y == py:
                continue
            side = abs(y - py)
            # Check both directions
            for dx in [side, -side]:
                nx = px + dx
                total += (self.points[(px, y)] *
                          self.points[(nx, py)] *
                          self.points[(nx, y)])
        return total
# ds = DetectSquares()
# ds.add([3,10]); ds.add([11,2]); ds.add([3,2])
# ds.count([11,10]) → 1

Q: 392. What is the Design a Rate Limiter (Sliding Window Log)?
Write the implementation in Python.
A: Store timestamps of requests in a sorted structure. For each new request, remove expired timestamps and check if count < limit.
Time: O(log n) per request
from collections import deque
import time
class RateLimiter:
    def __init__(self, max_requests, window_seconds):
        self.max_req = max_requests
        self.window = window_seconds
        self.logs = {}  # user_id → deque of timestamps
    def allow_request(self, user_id, timestamp=None):
        if timestamp is None:
            timestamp = time.time()
        if user_id not in self.logs:
            self.logs[user_id] = deque()
        log = self.logs[user_id]
        # Remove expired entries
        cutoff = timestamp - self.window
        while log and log[0] <= cutoff:
            log.popleft()
        if len(log) < self.max_req:
            log.append(timestamp)
            return True
        return False
# Token Bucket variant
class TokenBucket:
    def __init__(self, rate, capacity):
        self.rate = rate  # tokens per second
        self.capacity = capacity
        self.tokens = capacity
        self.last = time.time()
    def allow(self):
        now = time.time()
        self.tokens = min(self.capacity,
                          self.tokens + (now - self.last) * self.rate)
        self.last = now
        if self.tokens >= 1:
            self.tokens -= 1
            return True
        return False

Q: 393. What is the Alien Dictionary problem (Topological Sort on characters)?
Write a clean BFS implementation in Python.
A: Compare adjacent words to derive character ordering edges. Topological sort the resulting DAG. If a cycle exists, the order is invalid.
Time: O(C) total chars  |  Space: O(U + E) unique chars + edges
from collections import deque, defaultdict
def alien_order(words):
    adj = defaultdict(set)
    in_deg = {c: 0 for w in words for c in w}
    for i in range(len(words) - 1):
        w1, w2 = words[i], words[i+1]
        if len(w1) > len(w2) and w1[:len(w2)] == w2:
            return ""  # invalid: prefix comes after
        for a, b in zip(w1, w2):
            if a != b:
                if b not in adj[a]:
                    adj[a].add(b)
                    in_deg[b] += 1
                break
    queue = deque(c for c in in_deg if in_deg[c] == 0)
    result = []
    while queue:
        c = queue.popleft()
        result.append(c)
        for nb in adj[c]:
            in_deg[nb] -= 1
            if in_deg[nb] == 0:
                queue.append(nb)
    return ''.join(result) if len(result) == len(in_deg) else ""
# alien_order(["wrt","wrf","er","ett","rftt"]) → "wertf"

Q: 394. What is the Clone Graph (DFS) problem?
Write the implementation in Python.
A: Use a hash map from original → clone. DFS into each neighbor; if already cloned, return the cached copy.
Time: O(V + E)  |  Space: O(V)
class Node:
    def __init__(self, val=0, neighbors=None):
        self.val = val
        self.neighbors = neighbors or []
def clone_graph(node):
    if not node:
        return None
    cloned = {}
    def dfs(n):
        if n in cloned:
            return cloned[n]
        copy = Node(n.val)
        cloned[n] = copy
        for nb in n.neighbors:
            copy.neighbors.append(dfs(nb))
        return copy
    return dfs(node)

Q: 395. What is the Word Ladder (BFS shortest transformation)?
Write the optimized implementation in Python.
A: BFS where each state is a word. Generate neighbors by changing one letter at a time. Use a set for O(1) lookup of valid words.
Time: O(M² · N) M=word len, N=word list size
from collections import deque
def ladder_length(begin, end, word_list):
    word_set = set(word_list)
    if end not in word_set:
        return 0
    queue = deque([(begin, 1)])
    visited = {begin}
    while queue:
        word, steps = queue.popleft()
        for i in range(len(word)):
            for c in 'abcdefghijklmnopqrstuvwxyz':
                next_word = word[:i] + c + word[i+1:]
                if next_word == end:
                    return steps + 1
                if next_word in word_set and next_word not in visited:
                    visited.add(next_word)
                    queue.append((next_word, steps + 1))
    return 0
# ladder_length("hit","cog",["hot","dot","dog","lot","log","cog"])
# → 5: hit→hot→dot→dog→cog

Q: 396. What is the Flatten a Multilevel Doubly Linked List problem?
Write the implementation in Python.
A: When a node has a child, recursively flatten the child list and insert it between the current node and its next.
Time: O(n)  |  Space: O(d) depth
class Node:
    def __init__(self, val=0, prev=None, next=None, child=None):
        self.val = val
        self.prev = prev
        self.next = next
        self.child = child
def flatten(head):
    curr = head
    while curr:
        if curr.child:
            child_head = curr.child
            # Find tail of child list
            child_tail = child_head
            while child_tail.next:
                child_tail = child_tail.next
            # Splice child list
            nxt = curr.next
            curr.next = child_head
            child_head.prev = curr
            curr.child = None
            child_tail.next = nxt
            if nxt:
                nxt.prev = child_tail
        curr = curr.next
    return head

Q: 397. What is the Evaluate Division (graph BFS/DFS) problem?
Write the implementation in Python.
A: Model equations as a weighted directed graph: a/b=v means edge a→b with weight v and b→a with weight 1/v. BFS/DFS to find paths.
Time: O(Q · (V + E)) per query
from collections import defaultdict, deque
def calc_equation(equations, values, queries):
    graph = defaultdict(dict)
    for (a, b), v in zip(equations, values):
        graph[a][b] = v
        graph[b][a] = 1.0 / v
    def bfs(src, dst):
        if src not in graph or dst not in graph:
            return -1.0
        if src == dst:
            return 1.0
        visited = {src}
        queue = deque([(src, 1.0)])
        while queue:
            node, prod = queue.popleft()
            for nb, w in graph[node].items():
                if nb == dst:
                    return prod * w
                if nb not in visited:
                    visited.add(nb)
                    queue.append((nb, prod * w))
        return -1.0
    return [bfs(a, b) for a, b in queries]
# equations = [["a","b"],["b","c"]], values = [2.0, 3.0]
# queries = [["a","c"],["b","a"],["a","e"]]
# → [6.0, 0.5, -1.0]

Q: 398. What is the Longest Substring with Same Letters after K Replacements?
Write the sliding window solution in Python.
A: Track the most frequent character in the window. If window_size - max_freq > k, shrink the left side.
Time: O(n)  |  Space: O(26)
def character_replacement(s, k):
    freq = {}
    max_freq = 0
    left = 0
    max_len = 0
    for right in range(len(s)):
        freq[s[right]] = freq.get(s[right], 0) + 1
        max_freq = max(max_freq, freq[s[right]])
        # Window invalid: need more than k replacements
        if (right - left + 1) - max_freq > k:
            freq[s[left]] -= 1
            left += 1
        max_len = max(max_len, right - left + 1)
    return max_len
# character_replacement("AABABBA", 1) → 4
# Window "AABA" → replace one B → "AAAA"

Q: 399. What is the Maximum Number of Events Attended (Greedy + Heap)?
Write the implementation in Python.
A: Sort events by start day. Each day, push all available events onto a min-heap by end day. Attend the one ending soonest.
Time: O(n log n)  |  Space: O(n)
import heapq
def max_events(events):
    events.sort()
    heap = []
    i = 0
    n = len(events)
    attended = 0
    day = 1
    while i < n or heap:
        if not heap:
            day = events[i][0]
        while i < n and events[i][0] <= day:
            heapq.heappush(heap, events[i][1])
            i += 1
        # Remove expired events
        while heap and heap[0] < day:
            heapq.heappop(heap)
        if heap:
            heapq.heappop(heap)  # attend
            attended += 1
        day += 1
    return attended
# max_events([[1,2],[2,3],[3,4]]) → 3
# max_events([[1,2],[2,3],[3,4],[1,2]]) → 4

Q: 400. What is the Valid Sudoku check?
Write the implementation in Python.
A: Check that each row, column, and 3×3 box has no duplicate digits. Use sets to track seen values.
Time: O(81) → O(1)  |  Space: O(81)
def is_valid_sudoku(board):
    rows = [set() for _ in range(9)]
    cols = [set() for _ in range(9)]
    boxes = [set() for _ in range(9)]
    for i in range(9):
        for j in range(9):
            val = board[i][j]
            if val == '.':
                continue
            box_idx = (i // 3) * 3 + j // 3
            if (val in rows[i] or
                val in cols[j] or
                val in boxes[box_idx]):
                return False
            rows[i].add(val)
            cols[j].add(val)
            boxes[box_idx].add(val)
    return True

Q: 401. What is the Trap Rain Water using a Stack?
Write the stack-based solution in Python.
A: Use a monotonic decreasing stack. When a taller bar is found, pop and compute the water trapped between the current bar and the new stack top.
Time: O(n)  |  Space: O(n)
def trap_stack(height):
    stack = []  # indices
    water = 0
    for i, h in enumerate(height):
        while stack and height[stack[-1]] < h:
            bottom = height[stack.pop()]
            if not stack:
                break
            width = i - stack[-1] - 1
            bounded = min(h, height[stack[-1]]) - bottom
            water += width * bounded
        stack.append(i)
    return water
# trap_stack([0,1,0,2,1,0,1,3,2,1,2,1]) → 6

Q: 402. What is the Smallest K Elements using a Max-Heap?
Write the implementation in Python.
A: Maintain a max-heap of size k. For each element, if it's smaller than the heap top, replace it. The heap holds the k smallest.
Time: O(n log k)  |  Space: O(k)
import heapq
def smallest_k(arr, k):
    if k >= len(arr):
        return sorted(arr)
    # Python has min-heap, negate for max-heap
    heap = [-x for x in arr[:k]]
    heapq.heapify(heap)
    for x in arr[k:]:
        if x < -heap[0]:
            heapq.heapreplace(heap, -x)
    return sorted(-x for x in heap)
# Also: heapq.nsmallest (uses same idea internally)
def smallest_k_builtin(arr, k):
    return heapq.nsmallest(k, arr)
# smallest_k([7, 10, 4, 3, 20, 15], 3) → [3, 4, 7]

Q: 403. What is the Merge Sort for Counting Inversions?
Write the clean implementation in Python.
A: During merge, when a right element is placed before remaining left elements, count those as inversions: inv += len(left) - i.
Time: O(n log n)  |  Space: O(n)
def count_inversions(arr):
    if len(arr) <= 1:
        return arr, 0
    mid = len(arr) // 2
    left, inv_l = count_inversions(arr[:mid])
    right, inv_r = count_inversions(arr[mid:])
    merged = []
    inversions = inv_l + inv_r
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            merged.append(left[i])
            i += 1
        else:
            merged.append(right[j])
            inversions += len(left) - i
            j += 1
    merged.extend(left[i:])
    merged.extend(right[j:])
    return merged, inversions
# _, inv = count_inversions([2, 4, 1, 3, 5])
# inv → 3 (pairs: (2,1), (4,1), (4,3))

Q: 404. What is the All O(1) Data Structure (Inc/Dec/Max/Min)?
Write the implementation in Python.
A: Use a doubly-linked list of count buckets and a hash map from key → bucket. Inc/Dec move keys between adjacent buckets.
All ops: O(1)
class Bucket:
    def __init__(self, count=0):
        self.count = count
        self.keys = set()
        self.prev = self.next = None
class AllOne:
    def __init__(self):
        self.head = Bucket(0)  # sentinel min
        self.tail = Bucket(float('inf'))  # sentinel max
        self.head.next = self.tail
        self.tail.prev = self.head
        self.key_bucket = {}  # key → bucket
    def _insert_after(self, prev_bucket, count):
        b = Bucket(count)
        b.prev = prev_bucket
        b.next = prev_bucket.next
        prev_bucket.next.prev = b
        prev_bucket.next = b
        return b
    def _remove_bucket(self, b):
        b.prev.next = b.next
        b.next.prev = b.prev
    def inc(self, key):
        if key in self.key_bucket:
            b = self.key_bucket[key]
            new_count = b.count + 1
            nxt = b.next
            if nxt.count != new_count:
                nxt = self._insert_after(b, new_count)
            nxt.keys.add(key)
            self.key_bucket[key] = nxt
            b.keys.discard(key)
            if not b.keys: self._remove_bucket(b)
        else:
            nxt = self.head.next
            if nxt.count != 1:
                nxt = self._insert_after(self.head, 1)
            nxt.keys.add(key)
            self.key_bucket[key] = nxt
    def dec(self, key):
        b = self.key_bucket[key]
        new_count = b.count - 1
        b.keys.discard(key)
        if new_count == 0:
            del self.key_bucket[key]
        else:
            prv = b.prev
            if prv.count != new_count:
                prv = self._insert_after(b.prev, new_count)
            prv.keys.add(key)
            self.key_bucket[key] = prv
        if not b.keys: self._remove_bucket(b)
    def get_max_key(self):
        b = self.tail.prev
        return next(iter(b.keys)) if b != self.head else ""
    def get_min_key(self):
        b = self.head.next
        return next(iter(b.keys)) if b != self.tail else ""

Q: 405. What is the Maximum Width of a Binary Tree?
Write the implementation in Python.
A: Use BFS with index tracking. For each node at index i, children are at 2i and 2i+1. Width = rightmost - leftmost + 1.
Time: O(n)  |  Space: O(n)
from collections import deque
def width_of_binary_tree(root):
    if not root:
        return 0
    max_width = 0
    queue = deque([(root, 0)])
    while queue:
        level_len = len(queue)
        _, first_idx = queue[0]
        for _ in range(level_len):
            node, idx = queue.popleft()
            if node.left:
                queue.append((node.left, 2 * idx))
            if node.right:
                queue.append((node.right, 2 * idx + 1))
        max_width = max(max_width, idx - first_idx + 1)
    return max_width
# Tree:       1
#           /   \
#          3     2
#         / \     \
#        5   3     9
# Width → 4 (level 3: positions 0,1,_,3)

Q: 406. What is the Boundary Traversal of a Binary Tree?
Write the implementation in Python.
A: Collect the left boundary (top-down, non-leaf), leaves (left to right), and right boundary (bottom-up, non-leaf).
Time: O(n)  |  Space: O(n)
def boundary_traversal(root):
    if not root:
        return []
    result = [root.val]
    def left_boundary(node):
        if not node or (not node.left and not node.right):
            return
        result.append(node.val)
        if node.left:
            left_boundary(node.left)
        else:
            left_boundary(node.right)
    def leaves(node):
        if not node:
            return
        if not node.left and not node.right:
            result.append(node.val)
            return
        leaves(node.left)
        leaves(node.right)
    def right_boundary(node):
        if not node or (not node.left and not node.right):
            return
        if node.right:
            right_boundary(node.right)
        else:
            right_boundary(node.left)
        result.append(node.val)  # bottom-up
    left_boundary(root.left)
    leaves(root.left)
    leaves(root.right)
    right_boundary(root.right)
    return result

Q: 407. What is the Right Side View of a Binary Tree?
Write the implementation in Python.
A: BFS level-by-level, keeping only the last node of each level. Alternatively, DFS visiting right child first.
Time: O(n)  |  Space: O(n)
from collections import deque
# BFS approach
def right_side_view(root):
    if not root:
        return []
    result = []
    queue = deque([root])
    while queue:
        level_size = len(queue)
        for i in range(level_size):
            node = queue.popleft()
            if i == level_size - 1:
                result.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
    return result
# DFS approach
def right_side_view_dfs(root):
    result = []
    def dfs(node, depth):
        if not node:
            return
        if depth == len(result):
            result.append(node.val)
        dfs(node.right, depth + 1)
        dfs(node.left, depth + 1)
    dfs(root, 0)
    return result

Q: 408. What is the Reorder List problem (L0→Ln→L1→Ln-1→...)?
Write the implementation in Python.
A: Three steps: find the middle, reverse second half, merge alternately. All in O(1) space.
Time: O(n)  |  Space: O(1)
def reorder_list(head):
    if not head or not head.next:
        return
    # 1. Find middle
    slow = fast = head
    while fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next
    # 2. Reverse second half
    prev = None
    curr = slow.next
    slow.next = None
    while curr:
        curr.next, prev, curr = prev, curr, curr.next
    # 3. Merge alternately
    first, second = head, prev
    while second:
        tmp1, tmp2 = first.next, second.next
        first.next = second
        second.next = tmp1
        first = tmp1
        second = tmp2
# 1→2→3→4→5 becomes 1→5→2→4→3

Q: 409. What is the LRU Cache using OrderedDict?
Write the clean implementation in Python.
A: Python's OrderedDict maintains insertion order and supports move_to_end. Pop from front for LRU eviction.
Get/Put: O(1)
from collections import OrderedDict
class LRUCache:
    def __init__(self, capacity):
        self.cap = capacity
        self.cache = OrderedDict()
    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)
        return self.cache[key]
    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.cap:
            self.cache.popitem(last=False)
# cache = LRUCache(2)
# cache.put(1, 1); cache.put(2, 2)
# cache.get(1) → 1
# cache.put(3, 3)  # evicts key 2
# cache.get(2) → -1

Q: 410. What is the Shortest Distance from All Buildings (multi-source BFS)?
Write the implementation in Python.
A: BFS from each building to compute distance sums. A cell is valid only if reachable by all buildings. Return the minimum sum.
Time: O(B · m · n) B=buildings
from collections import deque
def shortest_distance(grid):
    m, n = len(grid), len(grid[0])
    dist_sum = [[0]*n for _ in range(m)]
    reach_count = [[0]*n for _ in range(m)]
    buildings = 0
    for si in range(m):
        for sj in range(n):
            if grid[si][sj] == 1:
                buildings += 1
                visited = [[False]*n for _ in range(m)]
                queue = deque([(si, sj, 0)])
                visited[si][sj] = True
                while queue:
                    r, c, d = queue.popleft()
                    for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
                        nr, nc = r+dr, c+dc
                        if (0 <= nr < m and 0 <= nc < n
                            and not visited[nr][nc]
                            and grid[nr][nc] == 0):
                            visited[nr][nc] = True
                            dist_sum[nr][nc] += d + 1
                            reach_count[nr][nc] += 1
                            queue.append((nr, nc, d+1))
    result = float('inf')
    for i in range(m):
        for j in range(n):
            if grid[i][j] == 0 and reach_count[i][j] == buildings:
                result = min(result, dist_sum[i][j])
    return result if result != float('inf') else -1
# 0=empty, 1=building, 2=obstacle

Q: 411. What is the Time-Based Key-Value Store?
Write the implementation in Python.
A: Store (timestamp, value) pairs per key. Use binary search to find the value at the largest timestamp ≤ query timestamp.
Set: O(1)  |  Get: O(log n)
import bisect
class TimeMap:
    def __init__(self):
        self.store = {}  # key → ([timestamps], [values])
    def set(self, key, value, timestamp):
        if key not in self.store:
            self.store[key] = ([], [])
        self.store[key][0].append(timestamp)
        self.store[key][1].append(value)
    def get(self, key, timestamp):
        if key not in self.store:
            return ""
        times, vals = self.store[key]
        i = bisect.bisect_right(times, timestamp) - 1
        return vals[i] if i >= 0 else ""
# tm = TimeMap()
# tm.set("foo", "bar", 1)
# tm.set("foo", "bar2", 4)
# tm.get("foo", 1) → "bar"
# tm.get("foo", 3) → "bar"
# tm.get("foo", 4) → "bar2"

Q: 412. What is the Substring Concatenation of All Words (sliding window)?
Write the implementation in Python.
A: All words have the same length. Slide a window of total_words × word_length. Use a frequency map to match all words.
Time: O(n · w) n=text length, w=word length
from collections import Counter
def find_substring(s, words):
    if not s or not words:
        return []
    wlen = len(words[0])
    wcount = len(words)
    total = wlen * wcount
    target = Counter(words)
    result = []
    for offset in range(wlen):
        left = offset
        window = Counter()
        count = 0
        for right in range(offset, len(s) - wlen + 1, wlen):
            word = s[right:right + wlen]
            if word in target:
                window[word] += 1
                count += 1
                while window[word] > target[word]:
                    lw = s[left:left + wlen]
                    window[lw] -= 1
                    count -= 1
                    left += wlen
                if count == wcount:
                    result.append(left)
            else:
                window.clear()
                count = 0
                left = right + wlen
    return result
# find_substring("barfoothefoobarman", ["foo","bar"])
# → [0, 9]

Q: 413. What is the Count Complete Tree Nodes problem?
Write the O(log²n) solution in Python.
A: Compare left and right subtree heights. If equal, left subtree is perfect (2^h - 1 nodes). Recurse on the other side.
Time: O(log²n)  |  Space: O(log n)
def count_nodes(root):
    if not root:
        return 0
    left_h = right_h = 0
    l, r = root, root
    while l:
        left_h += 1
        l = l.left
    while r:
        right_h += 1
        r = r.right
    if left_h == right_h:
        return (1 << left_h) - 1  # perfect tree
    return 1 + count_nodes(root.left) + count_nodes(root.right)
# Complete tree with 6 nodes:
#       1
#      / \
#     2   3
#    / \ /
#   4  5 6
# count_nodes(root) → 6

Q: 414. What is the Find K Closest Elements problem?
Write the binary search solution in Python.
A: Binary search for the left boundary of the k-length window. Compare distances at the boundary to decide which side to shift.
Time: O(log(n-k) + k)  |  Space: O(1)
def find_closest_elements(arr, k, x):
    lo, hi = 0, len(arr) - k
    while lo < hi:
        mid = (lo + hi) // 2
        if x - arr[mid] > arr[mid + k] - x:
            lo = mid + 1
        else:
            hi = mid
    return arr[lo:lo + k]
# find_closest_elements([1,2,3,4,5], 4, 3) → [1,2,3,4]
# find_closest_elements([1,2,3,4,5], 4, -1) → [1,2,3,4]

Q: 415. What is the Shortest Palindrome (KMP trick)?
Write the implementation in Python.
A: Find the longest palindromic prefix by building s + '#' + reverse(s) and computing the KMP failure function. Prepend the missing suffix reversed.
Time: O(n)  |  Space: O(n)
def shortest_palindrome(s):
    if not s:
        return s
    rev = s[::-1]
    combined = s + '#' + rev
    # KMP failure function
    fail = [0] * len(combined)
    for i in range(1, len(combined)):
        j = fail[i-1]
        while j > 0 and combined[i] != combined[j]:
            j = fail[j-1]
        if combined[i] == combined[j]:
            j += 1
        fail[i] = j
    # Longest palindromic prefix length
    pal_len = fail[-1]
    return rev[:len(s) - pal_len] + s
# shortest_palindrome("aacecaaa") → "aaacecaaa"
# shortest_palindrome("abcd") → "dcbabcd"

Q: 416. What is the Minimum Operations to Make Array Equal?
Write the implementation in Python.
A: Array is [1,3,5,...,2n-1]. Target is the median (n). Sum of (n - arr[i]) for all i < n/2. Formula: n²/4.
Time: O(1)
# Closed-form
def min_operations(n):
    return n * n // 4
# Step-by-step understanding:
def min_operations_explained(n):
    arr = [2*i + 1 for i in range(n)]
    target = n  # median
    ops = sum(target - x for x in arr if x < target)
    return ops
# min_operations(3) → 2
# [1,3,5] → target 3: move 1→3 (2 ops)
# min_operations(6) → 9
# [1,3,5,7,9,11] → target 6

Q: 417. What is the Number of Provinces (connected components)?
Write Union-Find and DFS solutions in Python.
A: Count connected components in an adjacency matrix. Either DFS/BFS from each unvisited node, or use Union-Find and count distinct roots.
Time: O(n²)
# DFS approach
def find_provinces_dfs(isConnected):
    n = len(isConnected)
    visited = [False] * n
    provinces = 0
    def dfs(i):
        visited[i] = True
        for j in range(n):
            if isConnected[i][j] and not visited[j]:
                dfs(j)
    for i in range(n):
        if not visited[i]:
            dfs(i)
            provinces += 1
    return provinces
# Union-Find approach
def find_provinces_uf(isConnected):
    n = len(isConnected)
    parent = list(range(n))
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]
            x = parent[x]
        return x
    def union(a, b):
        pa, pb = find(a), find(b)
        if pa != pb:
            parent[pa] = pb
    for i in range(n):
        for j in range(i+1, n):
            if isConnected[i][j]:
                union(i, j)
    return len(set(find(i) for i in range(n)))
# find_provinces_dfs([[1,1,0],[1,1,0],[0,0,1]]) → 2

Q: 418. What is the Minimum Insertions to Make a String Palindrome?
Write the implementation in Python.
A: Equal to len(s) - Longest Palindromic Subsequence(s). LPS is the LCS of s and reverse(s).
Time: O(n²)  |  Space: O(n)
def min_insertions_palindrome(s):
    n = len(s)
    rev = s[::-1]
    # LCS of s and reverse(s) = LPS
    prev = [0] * (n + 1)
    for i in range(1, n + 1):
        curr = [0] * (n + 1)
        for j in range(1, n + 1):
            if s[i-1] == rev[j-1]:
                curr[j] = prev[j-1] + 1
            else:
                curr[j] = max(prev[j], curr[j-1])
        prev = curr
    lps = prev[n]
    return n - lps
# min_insertions_palindrome("abcde") → 4
# → "edcbabcde"
# min_insertions_palindrome("abcda") → 2
# → "adcbcda"

Q: 419. What is the Range Sum Query 2D - Immutable (2D prefix sum)?
Write the implementation in Python.
A: Precompute prefix[i][j] = sum of matrix[0..i-1][0..j-1]. Query any sub-rectangle using inclusion-exclusion.
Build: O(m·n)  |  Query: O(1)
class NumMatrix:
    def __init__(self, matrix):
        m, n = len(matrix), len(matrix[0])
        self.prefix = [[0]*(n+1) for _ in range(m+1)]
        for i in range(1, m+1):
            for j in range(1, n+1):
                self.prefix[i][j] = (matrix[i-1][j-1]
                    + self.prefix[i-1][j]
                    + self.prefix[i][j-1]
                    - self.prefix[i-1][j-1])
    def sum_region(self, r1, c1, r2, c2):
        return (self.prefix[r2+1][c2+1]
                - self.prefix[r1][c2+1]
                - self.prefix[r2+1][c1]
                + self.prefix[r1][c1])
# matrix = [[3,0,1,4,2],[5,6,3,2,1],
#           [1,2,0,1,5],[4,1,0,1,7]]
# nm = NumMatrix(matrix)
# nm.sum_region(2, 1, 4, 3) → error (use valid range)
# nm.sum_region(1, 1, 2, 2) → 6+3+2+0 = 11

Q: 420. What is the Sort Characters By Frequency problem?
Write the implementation in Python.
A: Count frequencies, then sort by frequency descending. Can use bucket sort for O(n) time.
Time: O(n) with bucket sort
from collections import Counter
def frequency_sort(s):
    freq = Counter(s)
    # Bucket sort approach
    n = len(s)
    buckets = [[] for _ in range(n + 1)]
    for char, count in freq.items():
        buckets[count].append(char)
    result = []
    for count in range(n, 0, -1):
        for char in buckets[count]:
            result.append(char * count)
    return ''.join(result)
# Simpler with sorted
def frequency_sort_simple(s):
    freq = Counter(s)
    return ''.join(c * f for c, f in freq.most_common())
# frequency_sort("tree") → "eert" or "eetr"
# frequency_sort("Aabb") → "bbAa" or "bbaA"

Q: 421. What is the Design a Stack with Increment Operation?
Write the lazy propagation solution in Python.
A: Store increments lazily in an auxiliary array. On pop, propagate the increment to the element below. Avoids O(k) per increment.
All ops: O(1)
class CustomStack:
    def __init__(self, maxSize):
        self.stack = []
        self.inc = []  # lazy increment array
        self.max_size = maxSize
    def push(self, x):
        if len(self.stack) < self.max_size:
            self.stack.append(x)
            self.inc.append(0)
    def pop(self):
        if not self.stack:
            return -1
        idx = len(self.stack) - 1
        val = self.stack.pop() + self.inc[idx]
        if idx > 0:
            self.inc[idx - 1] += self.inc[idx]
        self.inc.pop()
        return val
    def increment(self, k, val):
        idx = min(k, len(self.stack)) - 1
        if idx >= 0:
            self.inc[idx] += val
# st = CustomStack(3)
# st.push(1); st.push(2)
# st.increment(5, 100)  # add 100 to bottom 5 (all)
# st.pop() → 102
# st.pop() → 101

Q: 422. What is the Find All Numbers Disappeared in an Array?
Write the O(1) space solution in Python.
A: For each value, mark the corresponding index as negative. Positive indices at the end indicate missing numbers.
Time: O(n)  |  Space: O(1)
def find_disappeared(nums):
    for x in nums:
        idx = abs(x) - 1
        nums[idx] = -abs(nums[idx])
    return [i + 1 for i in range(len(nums)) if nums[i] > 0]
# Cyclic sort approach
def find_disappeared_cyclic(nums):
    i = 0
    while i < len(nums):
        correct = nums[i] - 1
        if nums[i] != nums[correct]:
            nums[i], nums[correct] = nums[correct], nums[i]
        else:
            i += 1
    return [i + 1 for i in range(len(nums)) if nums[i] != i + 1]
# find_disappeared([4,3,2,7,8,2,3,1]) → [5, 6]