# there are no results. if isinstance(subtrie, str): return [subtrie] if query[i:] == subtrie[i:len(subtrie)] else [] if query[i] not in subtrie.children: return [] subtrie = subtrie.children[query[i]] # subtrie now contains all completions of the query string. results = [] # Maintain a list of nodes we have not yet examined. This functions as a # stack; we could use recursion instead. unprocessedSubtries = [subtrie] while unprocessedSubtries: subtrie = unprocessedSubtries.pop() if isinstance(subtrie, str): # If this is a shortcut node, just add the string to the results. results.append(subtrie) else: # Otherwise, push all child nodes onto the stack to be examined. unprocessedSubtries.extend(subtrie.children.values()) return results # This function exists to sort the results of autocomplete (making it easier to automatically verify the results). # The problem statement does not specify any particular ordering for the returned strings, but we would like to be # able to specify a list as the expected result and then compare it directly to the actual result. def autocompleteTestHarness(query, dictionary): return sorted(autocomplete(query, dictionary)) testFunction(autocompleteTestHarness, testCases)
# Implementation without division; O(n) time, O(n) space # (note that any solution must use at least O(n) space as that much is needed to store the output) def calculateProducts(factors): # Each element of beforeProducts contains the product of all factors BEFORE that index. # For convenience, the special case of the first index (which has no factors before it) is assigned to 1. beforeProducts = [1 for _ in factors] # Similarly, afterProducts contains the product of all factors AFTER each index, # and the last index is assigned to 1. afterProducts = [1 for _ in factors] # Fill in the lists of beforeProducts and afterProducts. for i in range(0, len(factors) - 1): beforeProducts[i+1] = beforeProducts[i] * factors[i] for i in range(len(factors) - 1, 0, -1): afterProducts[i-1] = afterProducts[i] * factors[i] # Then, the output for each index is simply the product of all factors before that index # multiplied by the product of all factors after that index, both of which we already know! return [beforeProducts[i] * afterProducts[i] for i in range(len(factors))] testFunction(calculateProducts, testCases) # Note: The implementation with division would calculate the product of all the factors, # and then for each index, divide by the factor at that index to get the output. # The implementation is trivial, but breaks if any factor is 0 (due to division by 0). # Special cases could be constructed to get around this issue, but the end result would # likely be more complex than the no-division solution without providing any advantage # in space or time complexity.
def cons(a, b): def pair(f): return f(a, b) return pair # car solution def car(pair): return pair(lambda a, b: a) # cdr solution def cdr(pair): return pair(lambda a, b: b) # Testing code def carTestHarness(a, b): return car(cons(a, b)) def cdrTestHarness(a, b): return cdr(cons(a, b)) testFunction(carTestHarness, carTestCases) testFunction(cdrTestHarness, cdrTestCases)
# that index. while arr[i] > 0 and arr[i] <= len(arr) and arr[i] != i + 1: if arr[arr[i] - 1] == arr[i]: # Duplicate number, and one instance is already in the right place. # Break (otherwise infinite loop...) break arr[arr[i] - 1], arr[i] = arr[i], arr[arr[i] - 1] # Find the first un-populated index. In the case that the array is perfectly filled # from 1 to the size of the array, i will simply run all the way to len(arr) and # len(arr) + 1 will be returned at the end. i = 0 while i < len(arr): if arr[i] != i + 1: break i = i + 1 return i + 1 def fmiTestHarness(arr): # The problem statement requires that the solution run in constant space, # but may modify the input array in-place. Therefore, make a working copy # *before* calling the actual function so that the original input array # from the test case isn't modified and the output displays correctly. workingArr = arr[:] return firstMissingInteger(workingArr) testFunction(fmiTestHarness, testCases)
((17, [10, 15, 3, 7]), True), ((0, []), False), ((17, [5, 20, -3, 16]), True), ((17, [5, 20, -4, 16]), False), ] # Naive implementation: O(n^2) time, O(1) space (n = length of numbers list) def doTheyAddUp(k, numbers): for i in range(0, len(numbers) - 1): for j in range(i, len(numbers)): if (numbers[i] + numbers[j] == k): return True return False # One-pass implementation: O(n) time (assuming constant-time dictionary access), O(n) space (assuming reasonable dictionary implementation) def doTheyAddUp_OnePass(k, numbers): seen = {} for num in numbers: seen[num] = True if (k - num in seen): return True return False testFunction(doTheyAddUp, testCases) testFunction(doTheyAddUp_OnePass, testCases)
if not s[i] in currentLetterCounts: numUniqueLetters += 1 currentLetterCounts[s[i]] = 1 else: currentLetterCounts[s[i]] += 1 # If we've exceeded k, pull up the the other end behind us until we # drop a unique character again. # Note that while this is a nested loop, runtime is still O(n) because # the inner loop examines each index of the string at most once # globally. while numUniqueLetters > k: if currentLetterCounts[s[currentStart]] == 1: # Deleting letter counts as they reach zero allows us to # operate in O(k) space, since the dict will have at most # k+1 entries. del currentLetterCounts[s[currentStart]] numUniqueLetters -= 1 else: currentLetterCounts[s[currentStart]] -= 1 currentStart += 1 # Now we know the length of the longest substring that contains at most # k distinct characters and ends at index i. currentLength = i - currentStart + 1 if currentLength > longestLength: longestLength = currentLength return longestLength testFunction(longestSubstring, testCases)
# case that we take the new character as part of a 2-character encoding). # 3) There are no ways to interpret the new character (i.e. it is 0). # Although the problem statement excludes the possibility that 0 appears # at the left of the full message, which would render it invalid, 0 may # still appear anywhere else in the message. Because we already need to # track the "previous" value of N for case #2, it is convenient to say # that N for the new message is 0. Then if we see a 1 or a 2 on the next # iteration, the formula for case 2 still holds. def numDecodes(message): # N may start as 1, because we are guaranteed that the message is valid so # there must be at least one way to decode it. N = 1 prevN = 0 prevChar = '' for c in reversed(message): if c >= '3' and c <= '9' or c == '2' and prevChar >= '7' and prevChar <= '9': # N stays the same, and update prevN prevN = N elif c == '1' or c == '2': N, prevN = N + prevN, N elif c == '0': N, prevN = 0, N return N testFunction(numDecodes, testCases)
# Correct handling of empty list (([],), 0), # Correct handling of all negative numbers (([-5, -1, -1, -5],), 0), ] # O(n) time; O(1) space def largestSum(numbers): # Note that the smallest sum we should ever return is 0, which we would # obtain simply by choosing no numbers (in the case they were all negative). largestSumWithoutPrevious = 0 largestSumWithPrevious = 0 largestSumWithCurrent = 0 for number in numbers: # For each number we encounter, we can choose to use it or not. # If we choose to use it, then the largest sum we can get is the # current number plus the largest sum we could obtain without using # the previous number (since it is adjacent). If we choose not to use # it, then the largest sum we can get is simply the largest sum we can # get from all the numbers not including the current one. largestSumWithCurrent = max(largestSumWithoutPrevious + number, largestSumWithPrevious) # Now we simply shift the largest sum values backwards to set up for # the next iteration. largestSumWithoutPrevious, largestSumWithPrevious = largestSumWithPrevious, largestSumWithCurrent return largestSumWithCurrent testFunction(largestSum, testCases)
# Test full unival tree ((Node(0, Node(0), Node(0, Node(0, Node(0), Node(0)), Node(0))), ), 7), ] def numUnivalSubtrees_RecursiveHelper(root): # Returned tuple fields: # - Number of unival subtrees # - Whether this node is the root of a unival subtree if root == None: return (0, True) (leftSubtrees, leftIsUnival) = numUnivalSubtrees_RecursiveHelper(root.left) (rightSubtrees, rightIsUnival) = numUnivalSubtrees_RecursiveHelper(root.right) if leftIsUnival and rightIsUnival and ( root.left == None or root.left.value == root.value) and ( root.right == None or root.right.value == root.value): return (leftSubtrees + rightSubtrees + 1, True) return (leftSubtrees + rightSubtrees, False) def numUnivalSubtrees_Recursive(root): (numSubtrees, _) = numUnivalSubtrees_RecursiveHelper(root) return numSubtrees testFunction(numUnivalSubtrees_Recursive, testCases)