- Facebook Interview Questions
- Copied from Google (also used in Facebook interviews)
- 5. Longest Palindromic Substring
- 34. Find First and Last Position of Element in Sorted Array
- 56. Merge Intervals
- 297. Serialize and Deserialize Binary Tree
- 247. Strobogrammatic Number II
- 17. Letter Combinations of a Phone Number
- 146. LRU Cache
- Facebook Proper
- Part I. Arrays and Strings
- Part II. Linked lists
- 138. Copy List with Random Pointer (Facebook)
- Part III. Trees and Graphs
- 938. Range Sum of BST
- Trees and Graphs
- LeetCode Facebook problems not reviewed
- Part VI. Recursion
- 46. Permutations
- 47. Permutations II
- 301. Remove Invalid Parentheses
- 10. Regular Expression Matching
- 78. Subsets
- Part V. Sorting and Searching
- 29. Divide Two Integers
- 33. Search in Rotated Sorted Array
- 50. Pow(x, n)
- 162. Find Peak Element
- 278. First Bad Version
- 349. Intersection of Two Arrays
- 350. Intersection of Two Arrays II
- Part VII. Dynamic Programming
- 32. Longest Valid Parentheses
- 91. Decode Ways
- 121. Best Time to Buy and Sell Stock
- 139. Word Break
- 304. Range Sum Query 2D - Immutable
- 523. Continuous Subarray Sum
- Part VIII. Design
- 173. Binary Search Tree Iterator
- 211. Add and Search Word - Data structure design
- 421. Maximum XOR of Two Numbers in an Array
- Part XIX. Other
- 282. Expression Add Operators
- 438. Find All Anagrams in a String
- 567. Permutation in String (is it same as 438)
- 953. Verifying an Alien Dictionary
- 986. Interval List Intersections
- Part X. SQL
- 176. Second Highest Salary
- 177. Nth Highest Salary
- 183. Customers Who Never Order
- 597. Friend Requests I: Overall Acceptance Rate
- 602. Friend Requests II: Who Has the Most Friends
- 185. Department Top Three Salaries
- Check the Google notebook for the following problems that are also in Facebook Section
- Facebook Recruiting Portal Coding Questions
- 1. Arrays
- 2. Starter Plan
- 3. Strings
- 4. Heap
- 5. Greedy Algorithms
- 6. Trees
- 7. Search
- 8. Sorting
- 9. Recursion
- 10. Stack
- Linked Lists
- Graphs
Source notebook: algo09_META.ipynb
Facebook Interview Questions
Leetcode (numbered)
Other technical interview questions:
* zero out rows and columns that contain zero in a 2D matrix.
* Remove duplicates from array and
* Implement dot product (sum([i*j for (i, j) in zip(list1, list2)]))
* stack, queue
* LeetCode #20 but returning 0/1 instead of True/False (https://leetcode.com/problems/valid-parentheses/)
* Parse an nginx log file and sort based on most frequent lines
* Access a github API and count number of repos with specific words; learn an obscure API
* Let struct A{ int a; char b;...}. How can you get the offset of a, b
Interview questions: * What is your proudest technical accomplishment? * Describe a situation where you had to use persuasion to get someone to do something. What was effective? * Tell me about a time when you had to deal with pushback on your ideas from superior colleagues or stakeholders. * Describe when you deal with someone difficult, how you handle it?
from typing import List, Optional
# given array of continuous numbers differing by 1, find missing numbers in the sequence # t.c. O(n+(max(nums)−min(nums))), space O((max(nums)−min(nums))) - t.c. can be huge, consider intervals # [1, 2, 4, 5, 7] - 3, 6 are missing from typing import List def find_missing(nums: List) -> List: # edge cases if not (hasattr(nums, "__getitem__") and hasattr(nums, "__len__")): # if we use Iterable instead of List raise TypeError("Must be a sequence with indexing.") # needed to avoid Iterator - no len() if not isinstance(nums, Iterable): raise TypeError('Check type') if not all(isinstance(x, int) for x in nums): raise TypeError("All elements must be integers") if len(nums) < 2: return [] # initiate vars m = [] # iterate for idx in range(len(nums)-1): if nums[idx+1] - nums[idx] > 1: m.extend(list(range(nums[idx]+1, nums[idx+1]))) return m nums = [1, 2, 4, 5, 7] find_missing(nums)
[3, 6]
# longest_increasing_subsequence in a list def longest_increasing(nums: List) -> int: # edge cases if not (hasattr(nums, "__getitem__") and hasattr(nums, "__len__")): # if we use Iterable instead of List raise TypeError("Must be a sequence with indexing.") if not all(isinstance(x, (int, float)) for x in nums): raise TypeError("All elements must be numeric.") if not isinstance(nums, list): raise TypeError('m') if not nums: return 0 elif len(nums) == 1: return 1 # initiate max_len, cur_len = 1, 1 # iterate for i in range(len(nums)-1): if nums[i+1] > nums[i]: cur_len += 1 max_len = max(max_len, cur_len) else: cur_len = 1 return max_len nums = [1, 2, 4, 3, 5, 7, 9, 3, 2, 1, 2] longest_increasing(nums)
4
from typing import List def zero_out(matrix: List[List[int]]) -> List[List[int]]: """ Zeroes out all rows and columns of a 2D matrix where there is a zero. Args: matrix: A 2D matrix. Returns: A new 2D matrix with all rows and columns that contain a zero zeroed out. """ # edge cases # 1. Validate that matrix is not empty and truly a list of lists if not matrix: return [] if not isinstance(matrix, list) or any(not isinstance(row, list) for row in matrix): raise TypeError('Matrix must be a non-empty list of lists.') # (Optional) Check all elements are integers: if any(any(not isinstance(x, int) for x in row) for row in matrix): raise TypeError("All elements must be integers.") # 2. Make a copy if you really want a "new" matrix new_matrix = [row[:] for row in matrix] # initiate zero_rows = set() zero_cols = set() # iterate for i in range(len(matrix)): for j in range(len(matrix[i])): if j in zero_cols: continue if matrix[i][j]==0: zero_rows.add(i) zero_cols.add(j) for i in zero_rows: matrix[i] = [0]*len(matrix[0]) for j in zero_cols: for i in range(len(matrix)): matrix[i][j]=0 return matrix matrix = [ [1, 2, 3], [4, 5, 0], [6, 7, 8] ] zero_out(matrix)
[[1, 2, 0], [0, 0, 0], [6, 7, 0]]
https://stackoverflow.com/questions/61212929/getting-the-count-of-github-repositories-for-a-search-query
Copied from Google (also used in Facebook interviews)
5. Longest Palindromic Substring
Given a string s, return the longest palindromic substring in s. A string is called a palindrome string if the reverse of that string is the same as the original string.
Example 1: Input: s = "babad" Output: "bab" Explanation: "aba" is also a valid answer.
Example 2: Input: s = "cbbd" Output: "bb"
Constraints:
1 <= s.length <= 1000 s consist of only digits and English letters.
# time = O(n^2) because we iterate n centers O(n), expanding each left and right O(n) # space = O(1) def longest_ps(s: str) -> str: if not isinstance(s, str): raise ValueError("Input must be a string.") if len(s) < 2: return s def expand_center(left: int, right: int) -> tuple: while left >= 0 and right < len(s) and s[left] == s[right]: # expand until palindrome left -= 1 right += 1 return left + 1, right - 1 # return bounds of palindrome start, end = 0, 0 # start and end of longest palindrome for i in range(len(s)): # Check for odd-length palindromes (single character center) left1, right1 = expand_center(i, i) # Check for even-length palindromes (two character center) left2, right2 = expand_center(i, i + 1) # Update the longest palindrome found if right1 - left1 > end - start: start, end = left1, right1 if right2 - left2 > end - start: start, end = left2, right2 return s[start:end + 1]
34. Find First and Last Position of Element in Sorted Array
Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value.
If target is not found in the array, return [-1, -1].
You must write an algorithm with O(log n) runtime complexity.
Example 1: Input: nums = [5,7,7,8,8,10], target = 8 Output: [3,4]
Example 2: Input: nums = [5,7,7,8,8,10], target = 6 Output: [-1,-1]
Example 3: Input: nums = [], target = 0 Output: [-1,-1]
Constraints:
0 <= nums.length <= 105 -109 <= nums[i] <= 109 nums is a non-decreasing array. -109 <= target <= 109
Algorithm (bin search)
Define a function called findBound which takes three arguments: the array, the target to search for, and a boolean value isFirst which indicates if we are trying to find the first or the last occurrence of target.
We use 2 variables to keep track of the subarray that we are scanning. Let's call them begin and end. Initially, begin is set to 0 and end is set to the last index of the array.
We iterate until begin is greater than or equal to end.
At each step, we calculate the middle element mid = (begin + end) / 2. We use the value of the middle element to decide which half of the array we need to search.
nums[mid] == target
isFirst is true ~ This implies that we are trying to find the first occurrence of the element. If mid == begin or nums[mid - 1] != target, then we return mid as the first occurrence of the target. Otherwise, we update end = mid - 1
isFirst is false ~ This implies we are trying to find the last occurrence of the element. If mid == end or nums[mid + 1] != target, then we return mid as the last occurrence of the target. Otherwise, we update begin = mid + 1
nums[mid] > target ~ We update end = mid - 1 since we must discard the right side of the array as the middle element is greater than target.
nums[mid] < target ~ We update begin = mid + 1 since we must discard the left side of the array as the middle element is less than target.
We return a value of -1 at the end of our function which indicates that target was not found in the array.
# time = O(logN), space = O(1) class Solution: def searchRange(self, nums: List[int], target: int) -> List[int]: lower_bound = self.findBound(nums, target, True) if (lower_bound == -1): return [-1, -1] upper_bound = self.findBound(nums, target, False) return [lower_bound, upper_bound] def findBound(self, nums: List[int], target: int, isFirst: bool) -> int: N = len(nums) begin, end = 0, N - 1 while begin <= end: mid = int((begin + end) / 2) if nums[mid] == target: if isFirst: # we found our lower bound if mid == begin or nums[mid - 1] < target: return mid # search on left side for bound end = mid - 1 else: # we found our upper bound. if mid == end or nums[mid + 1] > target: return mid # search on right side for bound begin = mid + 1 elif nums[mid] > target: end = mid - 1 else: begin = mid + 1 return -1
56. Merge Intervals
Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.
Example 1: Input: intervals = [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlap, merge them into [1,6].
Example 2: Input: intervals = [[1,4],[4,5]] Output: [[1,5]] Explanation: Intervals [1,4] and [4,5] are considered overlapping.
Constraints:
1 <= intervals.length <= 104 intervals[i].length == 2 0 <= starti <= endi <= 104
Algorithm
First, we sort the list as described. Then, we insert the first interval into our merged list and continue considering each interval in turn as follows: If the current interval begins after the previous interval ends, then they do not overlap and we can append the current interval to merged. Otherwise, they do overlap, and we merge them by updating the end of the previous interval if it is less than the end of the current interval.
A simple proof by contradiction shows that this algorithm always produces the correct answer. First, suppose that the algorithm at some point fails to merge two intervals that should be merged. This would imply that there exists some triple of indices ii, jj, and kk in a list of intervals \text{ints}ints such that i < j < ki<j<k and (\text{ints[i]}ints[i], \text{ints[k]}ints[k]) can be merged, but neither (\text{ints[i]}ints[i], \text{ints[j]}ints[j]) nor (\text{ints[j]}ints[j], \text{ints[k]}ints[k]) can be merged. From this scenario follow several inequalities:
\begin{aligned} \text{ints[i].end} < \text{ints[j].start} \ \text{ints[j].end} < \text{ints[k].start} \ \text{ints[i].end} \geq \text{ints[k].start} \ \end{aligned} ints[i].end<ints[j].start ints[j].end<ints[k].start ints[i].end≥ints[k].start
We can chain these inequalities (along with the following inequality, implied by the well-formedness of the intervals: \text{ints[j].start} \leq \text{ints[j].end}ints[j].start≤ints[j].end) to demonstrate a contradiction:
\begin{aligned} \text{ints[i].end} < \text{ints[j].start} \leq \text{ints[j].end} < \text{ints[k].start} \ \text{ints[i].end} \geq \text{ints[k].start} \end{aligned} ints[i].end<ints[j].start≤ints[j].end<ints[k].start ints[i].end≥ints[k].start
Therefore, all mergeable intervals must occur in a contiguous run of the sorted list.
# time = O(nlogn), space = O(n) class Solution: def merge(self, intervals: List[List[int]]) -> List[List[int]]: intervals.sort(key=lambda x: x[0]) merged = [] for interval in intervals: # if the list of merged intervals is empty or if the current # interval does not overlap with the previous, simply append it. if not merged or merged[-1][1] < interval[0]: merged.append(interval) else: # otherwise, there is overlap, so we merge the current and previous # intervals. merged[-1][1] = max(merged[-1][1], interval[1]) return merged
297. Serialize and Deserialize Binary Tree
Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.
Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.
Clarification: The input/output format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.
Example 1:
Input: root = [1,2,3,null,null,4,5]
Output: [1,2,3,null,null,4,5]
Example 2: Input: root = [] Output: []
Constraints:
The number of nodes in the tree is in the range [0, 104]. -1000 <= Node.val <= 1000
Intuition
The serialization of a Binary Search Tree is essentially to encode its values and more importantly its structure. One can traverse the tree to accomplish the above task. And it is well know that we have two general strategies to do so:
Breadth First Search (BFS)
We scan through the tree level by level, following the order of height, from top to bottom. The nodes on higher level would be visited before the ones with lower levels.
Depth First Search (DFS)
In this strategy, we adopt the depth as the priority, so that one would start from a root and reach all the way down to certain leaf, and then back to root to reach another branch.
The DFS strategy can further be distinguished as preorder, inorder, and postorder depending on the relative order among the root node, left node and right node.
In this task, however, the DFS strategy is more adapted for our needs, since the linkage among the adjacent nodes is naturally encoded in the order, which is rather helpful for the later task of deserialization.
Therefore, in this solution, we demonstrate an example with the preorder DFS strategy.
# time = space = O(n) # Depth First Search (DFS) class TreeNode(object): """ Definition of a binary tree node.""" def __init__(self, x): self.val = x self.left = None self.right = None # Serialization class Codec: def serialize(self, root): """ Encodes a tree to a single string. :type root: TreeNode :rtype: str """ def rserialize(root, string): """ a recursive helper function for the serialize() function.""" # check base case if root is None: string += 'None,' else: string += str(root.val) + ',' string = rserialize(root.left, string) string = rserialize(root.right, string) return string return rserialize(root, '') # Deserialization class Codec: def deserialize(self, data): """Decodes your encoded data to tree. :type data: str :rtype: TreeNode """ def rdeserialize(data_list): """ a recursive helper function for deserialization.""" if data_list[0] == 'None': data_list.pop(0) return None root = TreeNode(data_list[0]) data_list.pop(0) root.left = rdeserialize(data_list) root.right = rdeserialize(data_list) return root data_list = data.split(',') root = rdeserialize(data_list) return root
247. Strobogrammatic Number II
Given an integer n, return all the strobogrammatic numbers that are of length n. You may return the answer in any order. A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down).
Example 1: Input: n = 2 Output: ["11","69","88","96"]
Example 2: Input: n = 1 Output: ["0","1","8"]
Constraints:
1 <= n <= 14
Hint:
Try to use recursion and notice that it should recurse with n - 2 instead of n - 1.
Algorithm
Initialize a data structure reversiblePairs, which contains all pairs of reversible digits.
Call and return the recursive function, generateStroboNumbers(n, finalLength), where the first argument indicates that the current call will generate all n-digit strobogrammatic numbers. The second argument indicates the length of the final strobogrammatic numbers that we will generate and will be used to check if we can add '0' to the beginning and end of a number.
Create a function generateStroboNumbers(n, finalLength) which will return all strobogrammatic numbers of n-digits:
Check for base cases, if n == 0 return an array with an empty string [""], otherwise if n == 1 return ["0", "1", "8"]. Call generateStroboNumbers(n - 2, finalLength) to get all the strobogrammatic numbers of (n-2) digits and store them in subAns. Initialize an empty array currStroboNums to store strobogrammatic numbers of n-digits. For each number in prevStroboNums we append all reversiblePairs at the beginning and the end except when the current reversible pair is '00' and n == finalLength (because we can't append '0' at the beginning of a number) and push this new number in ans. At the end of the function, return all the strobogrammatic numbers, i.e. currStroboNums
# time = N⋅5^(N/2+1), space = N⋅5^(N/2). Iterative approach (level order traversal) has the same complexity def findStrobogrammatic(n: int) -> List[str]: reversible_pairs = [ ['0', '0'], ['1', '1'], ['6', '9'], ['8', '8'], ['9', '6'] ] def generate_strobo_numbers(n, final_length): if n == 0: # 0-digit strobogrammatic number is an empty string. return [""] if n == 1: # 1-digit strobogrammatic numbers. return ["0", "1", "8"] prev_strobo_nums = generate_strobo_numbers(n - 2, final_length) curr_strobo_nums = [] for prev_strobo_num in prev_strobo_nums: for pair in reversible_pairs: if pair[0] != '0' or n != final_length: curr_strobo_nums.append(pair[0] + prev_strobo_num + pair[1]) return curr_strobo_nums return generate_strobo_numbers(n, n) findStrobogrammatic(2)
['11', '69', '88', '96']
17. Letter Combinations of a Phone Number
Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order.
A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.
Example 1: Input: digits = "23" Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
Example 2: Input: digits = "" Output: []
Example 3: Input: digits = "2" Output: ["a","b","c"]
Constraints:
0 <= digits.length <= 4 digits[i] is a digit in the range ['2', '9'].
Algorithm
As mentioned previously, we need to lock-in letters when we generate new letters. The easiest way to save state like this is to use recursion. Our algorithm will be as follows:
If the input is empty, return an empty array.
Initialize a data structure (e.g. a hash map) that maps digits to their letters, for example, mapping "6" to "m", "n", and "o".
Use a backtracking function to generate all possible combinations.
The function should take 2 primary inputs: the current combination of letters we have, path, and the index we are currently checking. As a base case, if our current combination of letters is the same length as the input digits, that means we have a complete combination. Therefore, add it to our answer, and backtrack. Otherwise, get all the letters that correspond with the current digit we are looking at, digits[index]. Loop through these letters. For each letter, add the letter to our current path, and call backtrack again, but move on to the next digit by incrementing index by 1. Make sure to remove the letter from path once finished with it.
# time = O(N * (4^N)), where N = len of digits, space = O(n) class Solution: def letterCombinations(self, digits: str) -> List[str]: # If the input is empty, immediately return an empty answer array if len(digits) == 0: return [] # Map all the digits to their corresponding letters letters = {"2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz"} def backtrack(index, path): # If the path is the same length as digits, we have a complete combination if len(path) == len(digits): combinations.append("".join(path)) return # Get the letters that the current digit maps to, and loop through them possible_letters = letters[digits[index]] for letter in possible_letters: # Add the letter to our current path path.append(letter) # Move on to the next digit backtrack(index + 1, path) # Backtrack by removing the letter before moving onto the next path.pop() # Initiate backtracking with an empty path and starting index of 0 combinations = [] backtrack(0, []) return combinations
# from comments class Solution: def letterCombinations(self, digits: str) -> List[str]: lookup = { "2": ["a", "b", "c"], "3": ["d", "e", "f"], "4": ["g", "h", "i"], "5": ["j", "k", "l"], "6": ["m", "n", "o"], "7": ["p", "q", "r", "s"], "8": ["t", "u", "v"], "9": ["w", "x", "y", "z"] } letter_lists = [] for ch in digits: letter_lists.append(lookup[ch]) while len(letter_lists) > 1: l1 = letter_lists.pop() l2 = letter_lists.pop() combos = [] for i in l1: for j in l2: combos.append(j + i) letter_lists.append(combos) return [] if not letter_lists else letter_lists[0]
146. LRU Cache
Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. Implement the LRUCache class:
LRUCache(int capacity) Initialize the LRU cache with positive size capacity. int get(int key) Return the value of the key if the key exists, otherwise return -1. void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key. The functions get and put must each run in O(1) average time complexity.
Example 1:
Input ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] Output [null, null, null, 1, null, -1, null, -1, 3, 4]
Explanation LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // cache is {1=1} lRUCache.put(2, 2); // cache is {1=1, 2=2} lRUCache.get(1); // return 1 lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3} lRUCache.get(2); // returns -1 (not found) lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3} lRUCache.get(1); // return -1 (not found) lRUCache.get(3); // return 3 lRUCache.get(4); // return 4
Constraints:
1 <= capacity <= 3000 0 <= key <= 104 0 <= value <= 105 At most 2 * 105 calls will be made to get and put
Intuition
We're asked to implement the structure which provides the following operations in \mathcal{O}(1)O(1) time :
Get the key / Check if the key exists
Put the key
Delete the first added key
The first two operations in O(1) time are provided by the standard hashmap, and the last one - by linked list
class LRUCache(object): def __init__(self, capacity): """ :type capacity: int """ def get(self, key): """ :type key: int :rtype: int """ def put(self, key, value): """ :type key: int :type value: int :rtype: None """ # Your LRUCache object will be instantiated and called as such: # obj = LRUCache(capacity) # param_1 = obj.get(key) # obj.put(key,value)
from collections import OrderedDict class LRUCache(OrderedDict): def __init__(self, capacity): """ :type capacity: int """ self.capacity = capacity def get(self, key): """ :type key: int :rtype: int """ if key not in self: return - 1 self.move_to_end(key) return self[key] def put(self, key, value): """ :type key: int :type value: int :rtype: void """ if key in self: self.move_to_end(key) self[key] = value if len(self) > self.capacity: self.popitem(last = False)
# time = O(1), space = O(capacity) class DLinkedNode(): def __init__(self): self.key = 0 self.value = 0 self.prev = None self.next = None class LRUCache(): def _add_node(self, node): """ Always add the new node right after head. """ node.prev = self.head node.next = self.head.next self.head.next.prev = node self.head.next = node def _remove_node(self, node): """ Remove an existing node from the linked list. """ prev = node.prev new = node.next prev.next = new new.prev = prev def _move_to_head(self, node): """ Move certain node in between to the head. """ self._remove_node(node) self._add_node(node) def _pop_tail(self): """ Pop the current tail. """ res = self.tail.prev self._remove_node(res) return res def __init__(self, capacity): """ :type capacity: int """ self.cache = {} self.size = 0 self.capacity = capacity self.head, self.tail = DLinkedNode(), DLinkedNode() self.head.next = self.tail self.tail.prev = self.head def get(self, key): """ :type key: int :rtype: int """ node = self.cache.get(key, None) if not node: return -1 # move the accessed node to the head; self._move_to_head(node) return node.value def put(self, key, value): """ :type key: int :type value: int :rtype: void """ node = self.cache.get(key) if not node: newNode = DLinkedNode() newNode.key = key newNode.value = value self.cache[key] = newNode self._add_node(newNode) self.size += 1 if self.size > self.capacity: # pop the tail tail = self._pop_tail() del self.cache[tail.key] self.size -= 1 else: # update the value. node.value = value self._move_to_head(node)
Facebook Proper
Part I. Arrays and Strings
3. Longest Substring Without Repeating Characters
Given a string s, find the length of the longest substring without repeating characters.
Example 1:
Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Example 2:
Input: s = "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.
Example 3:
Input: s = "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Notice that the answer must be a substring, "pwke" is a subsequence and not a substring.
Example 4:
Input: s = ""
Output: 0
Constraints: * 0 <= s.length <= 5 * 104 * s consists of English letters, digits, symbols and spaces
def lengthOfLongestSubstring(s: str) -> int: ''' My Solution (Accepted) Iterate over each char in string with some logic ''' curr, max_ = '', '' for idx, c in enumerate(s): if not curr: # add current char if curr is empty curr = c elif curr and c not in curr: # add current char if it's not in curr curr += c elif curr and curr[-1] == c: # reset curr if current char is also the last one in curr curr = c else: # current char in curr, but not the last one - find its index in s, temp_idx = idx - 1 # but not in curr(!) because curr may be shorter. Make curr include while s[temp_idx] != c: # everyting after (because there is no c) + c at current idx temp_idx -= 1 curr = s[temp_idx+1 : idx+1] max_ = max(curr, max_, key=len) return len(max_) def lengthOfLongestSubstring2(s: str) -> int: '''LeetCode solution beautified by me We use HashSet to store the characters in current window [i,j) (j = i initially). Then we slide the index j to the right. If it is not in the HashSet, we slide j further. Doing so until s[j] is already in the HashSet. At this point, we found the maximum size of substrings without duplicate characters start with index i. If we do this for all i, we get our answer ''' mapp = {c:0 for c in s} left = 0 max_len = 0 for right, c in enumerate(s): mapp[c] += 1 while mapp[c] > 1: mapp[ s[left] ] -= 1 left += 1 max_len = max(max_len, right - left + 1) return max_len s1 = 'abcbacbb' s2 = 'bbbbb' s3 = 'pwwkew' s4 = '' # 3,1,3,0 for s in [s1, s2, s3, s4]: print( lengthOfLongestSubstring(s), end=', ' ) print() for s in [s1, s2, s3, s4]: print( lengthOfLongestSubstring2(s), end=', ' )
3, 1, 3, 0, 3, 1, 3, 0,
def lengthOfLongestSubstring(s: str) -> int: ''' Another my solution Time c. = O(3n) = O(n) Space c. = O(2n) = O(n)? ''' def all_unique(s2: str) -> bool: stack = [] for c in s2: if c not in stack: stack.append(c) return len(s2) == len(stack) i,j = 0,1 max_len = 0 length = len(s) while i < length and j < length: curr_str = s[i:j] if all_unique(curr_str): curr_len = len(curr_str) if curr_len > max_len: max_len = curr_len j += 1 else: while i < j and not all_unique(s[i:j]): i += 1 return max_len def lengthOfLongestSubstring2(s: str) -> int: ''' Brute force Time c. = O((n^2)/2) = O(n^2) Space c. = O(n) because of curr_str? Or O(1)? ''' max_len = 0 for i in range(len(s)): for j in range(i+1, len(s)): curr_str = s[i:j] if len(curr_str) == len(set(curr_str)): curr_len = len(curr_str) if curr_len > max_len: max_len = curr_len return max_len s1 = 'abcbacbb' s2 = 'bbbbb' s3 = 'pwwkew' s4 = '' # 3,1,3,0 for s in [s1, s2, s3, s4]: print( lengthOfLongestSubstring(s), end=', ' ) print() for s in [s1, s2, s3, s4]: print( lengthOfLongestSubstring2(s), end=', ' )
3, 1, 3, 0, 3, 1, 3, 0,
8. String to int
Implement the myAtoi(string s) function, which converts a string to a 32-bit signed integer (similar to C/C++'s atoi function).
The algorithm for myAtoi(string s) is as follows:
- Ignore leading whitespace
- Check if the next character (if not already at the end of the string) is '-' or '+' - determines if the final result is negative or positive, respectively. Assume '+' if neither is present.
- Read in digits, ignore the rest of the string after the digits. Convert digits to a number. If no digits were read, then the integer is 0. Change the sign as necessary.
- Make sure the integer is in the 32-bit signed integer range [-231, 231 - 1] - clamp it if not: integers less than -231 should be clamped to -231, greater than 231 - 1 to 2**31 - 1.
- Return the integer as the final result.
Note: * Only the space character ' ' is considered a whitespace character. * Do not ignore any characters other than the leading whitespace or the rest of the string after the digits.
Example 5:
Input: s = "-91283472332"
Output: -2147483648
Explanation:
Step 1: "-91283472332" (no characters read because there is no leading whitespace)
^
Step 2: "-91283472332" ('-' is read, so the result should be negative)
^
Step 3: "-91283472332" ("91283472332" is read in)
^
The parsed integer is -91283472332.
Since -91283472332 is less than the lower bound of the range [-231, 231 - 1], the final result is clamped to -231 = -2147483648.
Constraints: * 0 <= s.length <= 200 * s consists of English letters (lower-case and upper-case), digits (0-9), ' ', '+', '-', and '.'.
# LeetCode provides no solution for this import string def myAtoi(s: str) -> int: ''' My accepted solution. LeetCode doesn't provide a solution ''' s = s.strip() signs = '-+' if s[0] in signs: sign = s[0] s = s[1:] else: sign = '+' nums = [] for idx, c in enumerate(s): if c in string.digits: nums.append(c) else: break if not nums: return 0 res = 0 for idx, num in enumerate(nums): power = len(nums) - (idx + 1) res += int(num)*10**power if sign == '-': res = -res if res < -2**31: res = -2**31 elif res > 2**31 - 1: res = 2**31 - 1 return res n1 = '42' n2 = '-42' n3 = '4193 with words' n4 = 'words and 987' n5 = '-91283472332' for num in [n1, n2, n3, n4, n5]: print(myAtoi(num))
42 -42 4193 0 -2147483648
13. Roman to Integer
Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M.
Symbol Value
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II.
Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: * I can be placed before V (5) and X (10) to make 4 and 9. * X can be placed before L (50) and C (100) to make 40 and 90. * C can be placed before D (500) and M (1000) to make 400 and 900.
Given a roman numeral, convert it to an integer.
Example 1:
Input: s = "III"
Output: 3
Example 2:
Input: s = "IV"
Output: 4
Example 3:
Input: s = "IX"
Output: 9
Example 4:
Input: s = "LVIII"
Output: 58
Explanation: L = 50, V= 5, III = 3.
Example 5:
Input: s = "MCMXCIV"
Output: 1994
Explanation: M = 1000, CM = 900, XC = 90 and IV = 4.
Constraints: * 1 <= s.length <= 15 * s contains only the characters ('I', 'V', 'X', 'L', 'C', 'D', 'M'). * It is guaranteed that s is a valid roman numeral in the range [1, 3999].
def romanToInt(s: str) -> int: s = s.strip() conv = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000, 'IV': 4, 'IX': 9, 'XL': 40, 'XC': 90, 'CD': 400, 'CM': 900, } res = 0 i = 0 while i < len(s)-1: if s[i:i+2] in conv: res += conv[ s[i:i+2] ] i += 2 else: res += conv[ s[i] ] i += 1 if i == len(s)-1: res += conv[s[i]] return res def romanToInt2(s: str) -> int: ''' Leetcode solution - runs a bit faster ''' values = { "I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000, } total = 0 i = 0 while i < len(s): if i+1 < len(s) and values[ s[i] ] < values[ s[i+1] ]: # subtractive case ('IX') total += values[ s[i+1] ] - values[ s[i] ] i += 2 else: total += values[s[i]] i += 1 return total s1 = 'III' s2 = 'IV' s3 = 'IX' s4 = 'LVII' s5 = 'MCMXCIV' for s in [s1, s2, s3, s4, s5]: print( romanToInt(s) ) for s in [s1, s2, s3, s4, s5]: print( romanToInt2(s) )
3 4 9 57 1994 3 4 9 57 1994
15. 3Sum
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.
Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Example 2:
Input: nums = []
Output: []
Example 3:
Input: nums = [0]
Output: []
Constraints: * 0 <= nums.length <= 3000 * -105 <= nums[i] <= 105
Complexity * Brute force - three nested loops. Time c. O(n^3) * Optimal time c. - sorting = O(nlogn); nested loop = O(n^2)/2 => O(n^2). Therefore, O(nlogn + n^2) => O(n^2) * Space c. - no duplicates and i != j != k => total number of possible triplets is n/3. O(n/3) => O(n)
def threeSum(nums: List[int]) -> List[List[int]]: ''' My accepted solution Your runtime beats 93.44% of python3 submissions Your memory usage beats 98.69 % of python3 submissions ''' n = len(nums) nums.sort() res = set() # helps avoid duplicates and decrease scpace complexity for i in range( n-1 ): if nums[i] > 0 or (i > 0 and nums[i] == nums[i-1]): # next vals cannot sum to 0 in sorted arr if num[i] > 0 continue # it's a duplicate if nums[i] == nums[i-1] # helps greatly decrease run time (per LeetCode metrics) l = i+1 r = n-1 while l < r: sum_ = nums[i] + nums[l] + nums[r] if sum_ < 0: l += 1 elif sum_ > 0: r -= 1 else: res.add( (nums[i], nums[l], nums[r]) ) l += 1 r -= 1 return list(res) a1 = [-1, 0, 1, 2, -1, -4] a2 = [] a3 = [0] for a in [a1, a2, a3]: print( threeSum(a) )
[(-1, -1, 2), (-1, 0, 1)] [] []
26. Remove Duplicates from Sorted Array
- Integer array nums sorted in non-decreasing order, remove the duplicates in-place (only uniques left), keep the relative order.
- Since in some languages you can't change the array's length - move your result into the first part of the array nums (if there are k elements ramaining, the first k elements of nums should hold the final result) - it does not matter what's beyond the first k elements. Return k.
- Do not allocate extra space - O(1) extra memory, in-place.
- Custom Judge: check k and the array
Example 1:
Input: nums = [1,1,2]
Output: 2, nums = [1,2,_]
Explanation: Your function should return k = 2, with the first two elements of nums being 1 and 2 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).
Example 2:
Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4,,,,,_]
Explanation: Your function should return k = 5, with the first five elements of nums being 0, 1, 2, 3, and 4 respectively. It does not matter what you leave beyond the returned k (hence they are underscores).
Constraints: * 0 <= nums.length <= 3 * 104 * -100 <= nums[i] <= 100 * nums is sorted in non-decreasing order.
def removeDuplicates(nums: List[int]) -> int: ''' My accepted solution - moves arr elems, but doesn't modify them Your runtime beats 46.14 % of python3 submissions Your memory usage beats 45.38 % of python3 submissions ''' if not nums: return 0 elif len(set(nums)) == 1: # covers cases like [1,1], [1,1,1], etc. return 1 i = 0 j = 1 while j < len(nums) and i < len(nums): if i == j: j += 1 if nums[j] == nums[i]: # j moves faster, skipping duplicates j += 1 elif nums[j] > nums[i]: nums[i+1], nums[j] = nums[j], nums[i+1] # if found greater elem than i, swap with i+1 j += 1 i += 1 return i + 1 def removeDuplicates2(nums: List[int]) -> int: ''' LeetCode solution (translated into Python) - modifies arr by overwriting its elems in the first half with the correct elems moved from the second half Your runtime beats 51.32 % of python3 submissions Your memory usage beats 45.38 % of python3 submissions. ''' if not nums: return 0 k = 0 for i in range(1, len(nums)): if nums[i] != nums[k]: k += 1 nums[k] = nums[i] return k + 1 print('My solution') nums = [1,1,2] print( removeDuplicates(nums) ) print( nums ) nums = [0,0,1,1,1,2,2,3,3,4] print( removeDuplicates(nums) ) print( nums ) print('LeetCode solution') nums = [1,1,2] print( removeDuplicates2(nums) ) print( nums ) nums = [0,0,1,1,1,2,2,3,3,4] print( removeDuplicates2(nums) ) print( nums )
My solution 2 [1, 2, 1] 5 [0, 1, 2, 3, 4, 0, 2, 1, 3, 1] LeetCode solution 2 [1, 2, 2] 5 [0, 1, 2, 3, 4, 2, 2, 3, 3, 4]
977. Squares of a Sorted Array (FB interview per LeetCode's Discussions)
Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order.
Example 1:
Input: nums = [-4,-1,0,3,10]
Output: [0,1,9,16,100]
Explanation: After squaring, the array becomes [16,1,0,9,100].
After sorting, it becomes [0,1,9,16,100].
Example 2:
Input: nums = [-7,-3,2,3,11]
Output: [4,9,9,49,121]
Constraints: * 1 <= nums.length <= 104 * -104 <= nums[i] <= 104 * nums is sorted in non-decreasing order.
Follow up: Squaring, then sorting is very trivial. O(n) solution?
Intuition
Array A is sorted => negative elems with squares in decreasing order and non-negative elements with squares in increasing order.
For example, with [-3, -2, -1, 4, 5, 6], we have the negative part [-3, -2, -1] with squares [9, 4, 1], and the positive part [4, 5, 6] with squares [16, 25, 36]. Our strategy is to iterate over the negative part in reverse, and the positive part in the forward direction.
Algorithm
We can use two pointers to read the positive and negative parts of the array - one pointer j in the positive direction, and another i in the negative direction.
Now that we are reading two increasing arrays (the squares of the elements), we can merge these arrays together using a two-pointer technique
def sortedSquares(nums: List[int]) -> List[int]: ''' LeetCode solution ''' n = len(nums) result = [0] * n left = 0 right = n - 1 for i in range(n - 1, -1, -1): if abs(nums[left]) < abs(nums[right]): square = nums[right] right -= 1 else: square = nums[left] left += 1 result[i] = square * square return result
43. Multiply Strings
Myltiply two non-negative ints num1, num2 represented as str, return product as str. Cannot use libraries.
Example 1: Input: num1 = "2", num2 = "3" Output: "6"
Example 2: Input: num1 = "123", num2 = "456" Output: "56088"
Constraints: * 1 <= num1.length, num2.length <= 200 * num1 and num2 consist of digits only. * Both num1 and num2 do not contain any leading zero, except the number 0 itself
import math def multiply(num1: str, num2: str) -> str: ''' My accepted solution Your runtime beats 72.41 % of python3 submissions Too much memory Another solution - dict = {'0':0, '1':1, etc.} help avoid ord() and chr() ''' if num1=='0' or num2=='0': return '0' num1 = [ord(c)-ord('0') for c in num1] # convert to list of digits num1 = sum([e*(10**(len(num1)-idx-1)) for idx,e in enumerate(num1)]) # convert to number num2 = [ord(c)-ord('0') for c in num2] num2 = sum([e*(10**(len(num2)-idx-1)) for idx,e in enumerate(num2)]) product = num1*num2 order = math.floor(math.log10(product)) # to avoid math - keep doing n//10 and n%10 res = [] # until n//10 is 0 remainder = product for i in range(order,-1,-1): res.append(remainder//10**i) remainder = remainder%10**i return ''.join([chr(n+48) for n in res]) def multiply2(num1: str, num2: str) -> str: ''' Shorthand solution from Leetcode discussions - replace str(int) with my conversion to str without str() Your runtime beats 20.37 % of python3 submissions Your memory usage beats 55.03 % of python3 submissions ''' res = 0 for i, c1 in enumerate(num1[::-1]): for j, c2 in enumerate(num2[::-1]): res += (ord(c1)-ord('0')) * (ord(c2)-ord('0')) * (10**(i+j)) return str(res) num1 = '123' num2 = '456' multiply2(num1, num2)
'56088'
Best Leetcode Solution:


# best solution from Leetcode def multiply(num1: str, num2: str) -> str: ''' N and M - # digits in num1 and num2. Time c.: O(M⋅N) - N operations for each of M digits of the second number Space c.: O(M+N) - space to store output not included, but strings are immutable => a temp data structure, using O(M+N) space, is required to store the answer while it is updated. ''' if num1 == "0" or num2 == "0": return "0" # Initialize answer as a string of zeros of length N. N = len(num1) + len(num2) answer = [0] * N # Reverse num1 and num2 first_number = num1[::-1] second_number = num2[::-1] for place2, digit2 in enumerate(second_number): # For each digit in second_number multiply the digit by all digits in first_number. for place1, digit1 in enumerate(first_number): # The number of zeros from multiplying to digits depends on the place # of digit2 in second_number and the place of the digit1 in first_number. num_zeros = place1 + place2 # The digit currently at position numZeros in the answer string # is carried over and summed with the current result. carry = answer[num_zeros] multiplication = int(digit1) * int(digit2) + carry # Set the ones place of the multiplication result. answer[num_zeros] = multiplication % 10 # Carry the tens place of the multiplication result by # adding it to the next position in the answer array. answer[num_zeros + 1] += multiplication // 10 # Pop the excess 0 from the end of answer. if answer[-1] == 0: answer.pop() return ''.join(str(digit) for digit in reversed(answer))
# more concise Leetcode solution def multiply(num1, num2): """ :type num1: str :type num2: str :rtype: str """ if num1=='0' or num2=='0': return '0' n = len(num1) + len(num2) answer = [0]*n num11 = num1[::-1] num22 = num2[::-1] for idx2, digit2 in enumerate(num22): for idx1, digit1 in enumerate(num11): num_zeros = idx2 + idx1 carry = answer[ num_zeros ] product = int(digit1)*int(digit2) + carry answer[ num_zeros ] = product % 10 answer[ num_zeros+1 ] += product // 10 if answer[-1] == 0: answer.pop() return ''.join(str(d) for d in reversed(answer))
ord('1')-ord('0')
1
ord('A'), chr(48)
(65, '0')
49. Group Anagrams
Array of strs => group anagrams together (in any order). Anagram = word, phrase w/rearranged letters (typically original letters are used exactly once)
Example 1: Input: strs = ["eat","tea","tan","ate","nat","bat"] Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
Example 2: Input: strs = [""] Output: [[""]]
Example 3: Input: strs = ["a"] Output: [["a"]]
Constraints: * 1 <= strs.length <= 104 * 0 <= strs[i].length <= 100 * strs[i] consists of lowercase English letters.
from collections import defaultdict def groupAnagrams(strs: List[str]) -> List[List[str]]: ''' My accepted solution Your runtime beats 45% of python3 submissions Your memory usage beats 55% of python3 submissions Time c.: O(NKlogK), where N = len(strs), K = max_len of s in strs. Outer loop = O(N) (iterate over strs), then sort each s in O(KlogK) time Space c.: O(NK) - content of res ''' res = defaultdict(list) for s in strs: res[ ''.join(sorted(s)) ].append(s) return res.values() def groupAnagrams2(strs: List[str]) -> List[List[str]]: ''' Optimized O(NK) time and space c. solution from LeetCode. Two str = anagrams iff their char counts are the same ==> transform each str into char count consisting of a tuple of 26 non-negative integers, one per letter, which are used as keys in hash map ''' res = defaultdict(list) for s in strs: count = [0] * 26 for c in s: count[ ord(c)-ord('a') ] += 1 res[ tuple(count) ].append(s) return res.values() strs = ["eat","tea","tan","ate","nat","bat"] groupAnagrams2(strs)
dict_values([['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']])
76. Minimum Window Substring
Two strings s, t w/len m, n. Return minimum contiguous substring of s that includes each char in t (including duplicates). Return '' if none
Answer in testcases is unique.
Example 1:
Input: s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"
Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.
Example 2:
Input: s = "a", t = "a"
Output: "a"
Explanation: The entire string s is the minimum window.
Example 3:
Input: s = "a", t = "aa"
Output: ""
Explanation: Both 'a's from t must be included in the window.
Since the largest window of s only has one 'a', return empty string.
Constraints: * m == s.length * n == t.length * 1 <= m, n <= 105 * s and t consist of uppercase and lowercase English letters.
Follow up: Could you find an algorithm that runs in O(m + n) time?
Sliding Window Algorithm * Two pointers, left and righ initially pointing to first elem in S. * Expand window with right pointer until we get a desirable window (contains all chars from T, but may not be the shortest). * Contract window with left pointer while is still desirable. * If window is not desirable any more, start with step 2 again
def minWindow(s: str, t: str) -> str: ''' My solution from scratch Time c. O(3n) => O(n)? Space c. O(n) because of res? ''' def build_map(string: str) -> str: mapp = {} for c in string: if c in mapp: mapp[c] += 1 else: mapp[c] = 1 return mapp def is_map2_in_map1(map1: dict, map2: dict) -> bool: for k in map2: if k not in map1: return False if map1[k] < map2[k]: return False return True min_len = 10**5 res = '' map_t = build_map(t) l, r = 0, 1 while l < r and r < len(s)+1: if is_map2_in_map1(build_map(s[l:r]), map_t): if len(s[l:r]) < min_len: min_len = len(s[l:r]) res = s[l:r] l += 1 else: r += 1 return res
# my concise solution(based on Leetcode solution). Time/space c. O(n+m) from collections import Counter def minWindow(s, t): if not t or not s: return '' dict_t = Counter(t) # dict unique chars in t len_t = len(dict_t) # count unique chars in t dict_curr = {} # dict unique chars in curr win len_curr = 0 # count unique chars in curr window l,r = 0,0 res = float('inf'), None, None # shortest window length, l, r while r < len(s): char = s[r] dict_curr[ char ] = dict_curr.get(char, 0) + 1 if char in dict_t and dict_curr[char] == dict_t[char]: len_curr += 1 while l <= r and len_curr == len_t: char = s[l] if r - l + 1 < res[0]: res = (r - l + 1, l, r) dict_curr[ char ] -= 1 if char in dict_t and dict_curr[char] < dict_t[char]: len_curr -= 1 l += 1 r += 1 return '' if res[0]==float('inf') else s[ res[1]:res[2]+1 ]
# Leetcode solution from collections import Counter def minWindow(s: str, t: str) -> str: ''' LeetCode's solution. Time c. O(m+n) ''' if not t or not s: return '' l, r = 0, 0 dict_t = Counter(t) # count of all chars in t required = len(dict_t) # num of unique chars in t that must be in desired window formed = 0 # num unique chars from t in current window in desired frequency # e.g. if t=="AABC" => two A's, one B, one C => formed=3 window_counts = {} # count of all unique chars in current window ans = float("inf"), None, None # ans = tuple(window length, left, right) while r < len(s): char = s[r] # add one char from right window_counts[ char ] = window_counts.get(char, 0) + 1 if char in dict_t and window_counts[ char ] == dict_t[ char ]: formed += 1 # if current char's frequency == desired count, increment formed while l <= r and formed == required: # contract current window till until it's not 'desirable' char = s[l] if r - l + 1 < ans[0]: # smallest window until now ans = (r - l + 1, l, r) window_counts[char] -= 1 # char at `left` no longer part of current window if char in dict_t and window_counts[ char ] < dict_t[ char ]: formed -= 1 l += 1 # contract current window to look for new one r += 1 # keep expanding once contracting is done return s[ ans[1]:ans[2]+1 ] if not ans[0]==float("inf") else '' s = "ADOBECODEBANC" t = "ABC" minWindow(s, t)
'BANC'
A small improvement to the above approach can reduce the time complexity of the algorithm to O(2∗∣filtered_S∣+∣T∣), where filtered_S is the string formed from S by removing all the elements not present in T. This complexity reduction is evident when |filtered_S| <<< |S|∣filtered_S∣<<<∣S∣
# Leetcode solution 2 def minWindow(s: str, t: str) -> str: """ Optimized Leetcode solution """ if not t or not s: return "" dict_t = Counter(t) required = len(dict_t) # Leave only chars from s that occur in t filtered_s = [] for i, char in enumerate(s): if char in dict_t: filtered_s.append((i, char)) l, r = 0, 0 formed = 0 window_counts = {} ans = float("inf"), None, None # Same sliding window approach, but on as small list while r < len(filtered_s): character = filtered_s[r][1] window_counts[character] = window_counts.get(character, 0) + 1 if window_counts[character] == dict_t[character]: formed += 1 while l <= r and formed == required: character = filtered_s[l][1] end = filtered_s[r][0] start = filtered_s[l][0] if end - start + 1 < ans[0]: ans = (end - start + 1, start, end) window_counts[character] -= 1 if window_counts[character] < dict_t[character]: formed -= 1 l += 1 r += 1 return s[ ans[1]:ans[2]+1 ] if not ans[0]==float("inf") else ''
samples = [ ("ADOBECODEBANC", "ABC"), ("a", "a"), ("a", "aa") ] for sample in samples: print(minWindow(sample[0], sample[1])) #Output: "BANC" #Output: "a" #Output: ""
BANC a
88. Merge Sorted Array
Two int non-decreasing arrays nums1 and nums2 (m and n - num elements in nums1 and nums2 respectively). Merge then into a single non-decreasing array, store final array in nums1. To accommodate this, nums1 has a length of m + n, where the last n elements are 0s to be ignored. nums2 has a length of n.
Example 1: Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 Output: [1,2,2,3,5,6] Explanation: The arrays we are merging are [1,2,3] and [2,5,6]. The result of the merge is [1,2,2,3,5,6] with the underlined elements coming from nums1.
Example 2: Input: nums1 = [1], m = 1, nums2 = [], n = 0 Output: [1] Explanation: The arrays we are merging are [1] and []. The result of the merge is [1].
Example 3: Input: nums1 = [0], m = 0, nums2 = [1], n = 1 Output: [1] Explanation: The arrays we are merging are [] and [1]. The result of the merge is [1]. Note that because m = 0, there are no elements in nums1. The 0 is only there to ensure the merge result can fit in nums1.
Constraints: * nums1.length == m + n * nums2.length == n * 0 <= m, n <= 200 * 1 <= m + n <= 200 * -109 <= nums1[i], nums2[j] <= 109
Follow up: Can you come up with an algorithm that runs in O(m + n) time?
Naive approach: merge and sort - not taking advantage of the fact that the arrays are sorted
Using three pointers (i, j, m in my case) and moving from left requires an extra storage, as in my case too (time c. O(m+n), space c. O(m))
Best approach - three pointers and moving from the right! (time c. O(m+n), space c. O(1))
def merge(nums1: List[int], m: int, nums2: List[int], n: int) -> None: """ My accepted solution Your runtime beats 91.85 % of python3 submissions Your memory usage beats 85.57 % of python3 submissions """ if not nums2: return new_list = nums1 i, j = 0, 0 while i<m and j<n: if nums2[j] <= new_list[i]: new_list = new_list[:i] + [nums2[j]] + new_list[i:-1] j += 1 i += 1 m += 1 else: i += 1 if j < n: i = m while j < n: new_list[i] = nums2[j] i += 1 j += 1 # had to use new_list and this assignment to make it in-place nums1[:] = new_list def merge2(nums1: List[int], m: int, nums2: List[int], n: int) -> None: ''' Leetcode's solution. Three pointers, moving from the right Time c. O(m+n), space c. O(1) ''' p1, p2 = m - 1, n - 1 # p moves backwards, writing the smallest of p1 or p2. for p in range(n + m - 1, -1, -1): if p2 < 0: break if p1 >= 0 and nums1[p1] > nums2[p2]: nums1[p] = nums1[p1] p1 -= 1 else: nums1[p] = nums2[p2] p2 -= 1 nums1 = [1,2,3,0,0,0] m = 3 nums2 = [2,5,6] n = 3 merge2(nums1, m, nums2, n) print('Answer:', nums1)
Answer: [1, 2, 2, 3, 5, 6]
125. Valid Palindrome
Given a string s, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
Example 1: Input: s = "A man, a plan, a canal: Panama" Output: true Explanation: "amanaplanacanalpanama" is a palindrome
Example 2: Input: s = "race a car" Output: false Explanation: "raceacar" is not a palindrome.
Constraints: * 1 <= s.length <= 2 * 105 * s consists only of printable ASCII characters
import string def isPalindrome(s: str) -> bool: ''' My accepted solution ''' if not s: return False elif len(s) == 1: return True s = ''.join([c for c in s.lower() if c.isalnum()]) l, r = 0, len(s) - 1 while l < r: if not s[l] == s[r]: return False l += 1 r -= 1 return True def isPalindrome2(s: str) -> bool: ''' Leetcode solution (same approach) ''' i, j = 0, len(s) - 1 while i < j: while i < j and not s[i].isalnum(): i += 1 while i < j and not s[j].isalnum(): j -= 1 if s[i].lower() != s[j].lower(): return False i += 1 j -= 1 return True s1 = 'A man, a plan, a canal: Panama' s2 = 'race a car' isPalindrome(s1)
True
161. One Edit Distance
Given two strings s and t, return true if they are both one edit distance apart, otherwise return false. A string s is said to be one distance apart from a string t if you can: * Insert exactly one character into s to get t. * Delete exactly one character from s to get t. * Replace exactly one character of s with a different character to get t.
Example 1: Input: s = "ab", t = "acb" Output: true Explanation: We can insert 'c' into s to get t.
Example 2: Input: s = "", t = "" Output: false Explanation: We cannot get t from s by only one step.
Example 3: Input: s = "a", t = "" Output: true
Example 4: Input: s = "", t = "A" Output: true
Constraints: * 0 <= s.length <= 104 * 0 <= t.length <= 104 * s and t consist of lower-case letters, upper-case letters and/or digits.
def isOneEditDistance(s: str, t: str) -> bool: ''' My accepted solution. Time c. O(n), space c. O(n) because s[:-1] will create additional str (str=immutable) ''' # LENGTH NORMALIZATION if len(s) < len(t): s,t = t,s # EDGE CASES if (not s and not t) or (abs(len(s) - len(t)) > 1): return False elif (s[:-1] == t) or (s[1:] == t): return True # SAME LENGTH if len(s) == len(t): count = 0 for i in range(len(s)): if s[i] != t[i]: count += 1 #if count > 2: return False return True if count == 1 else False # LENGTH DIFFERENT BY ONE elif abs(len(s) - len(t)) == 1: count = 0 i, j = 0, 0 while i < len(s) and j < len(t): if s[i] != t[j]: count += 1 if count > 2: return False i += 1 else: i += 1 j += 1 return True if count == 1 else False def isOneEditDistance2(s: str, t: str) -> bool: ''' Shorter solution from comments (accepted). Time c. O(n), space c. O(1)? ''' if abs(len(s) - len(t)) > 1 or s == t: return False found = False i = j = 0 while i < len(s) and j < len(t): if s[i] != t[j]: if found: return False found = True if len(s) < len(t): i -= 1 # to differentiate from the case of equal lengths elif len(s) > len(t): j -= 1 i += 1 j += 1 return True s = 'ab' t = 'acb' print( isOneEditDistance(s, t) ) s = '' t = '' print( isOneEditDistance(s, t) ) s = 'a' t = '' print( isOneEditDistance(s, t) ) s = '' t = 'A' print( isOneEditDistance(s, t) )
True False True True
238. Product of Array Except Self
Given int array nums, return array answer where answer[i] = product of all elems of nums except nums[i]. Product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer (but not of entire nums?). Algo must run in O(n) time, without the division operation.
Example 1: Input: nums = [1,2,3,4] Output: [24,12,8,6]
Example 2: Input: nums = [-1,1,0,-3,3] Output: [0,0,9,0,0]
Constraints: * 2 <= nums.length <= 105 * -30 <= nums[i] <= 30 * The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer => cannot do product of all nums/nums[i]
Follow up: O(1) extra space complexity if not counting answer array?
def productExceptSelf3(nums: List[int]) -> List[int]: ''' My solution; works locally, exceeds time limit when submitted ''' answer = [] for i in range( len(nums) ): product = 1 for j in range( len(nums) ): if j != i: product *= nums[j] answer.append(product) return answer def productExceptSelf2(nums: List[int]) -> List[int]: ''' Leetcode solution 1. Time c. O(n), space c. O(n) because of two extra arrays ''' length = len(nums) L, R, answer = [0]*length, [0]*length, [0]*length L[0] = 1 # L[i] - product of all elems to left. L[0] = 1 since nothing to left for i in range(1, length): L[i] = nums[i-1] * L[i-1] R[-1] = 1 # R[i] - product of all elems to right. R[-1] = 1 since nothing to right for i in reversed(range(length - 1)): R[i] = nums[i + 1] * R[i + 1] print(L, R) for i in range(length): answer[i] = L[i] * R[i] return answer def productExceptSelf(nums: List[int]) -> List[int]: ''' Leetcode solution 2. Time c. O(n), space c. O(1). answer array = array L from solution 1 and var R contains a running product of elems to right, replacing array R from solution 1 ''' length = len(nums) answer = [0]*length answer[0] = 1 # answer[i] - product of all elems to left. answer[0] = 1 since nothing to left for i in range(1, length): answer[i] = nums[i - 1] * answer[i - 1] R = 1 # R - running product of elems to right. First R = 1 since nothing to right for i in reversed(range(length)): answer[i] = answer[i] * R R *= nums[i] return answer nums = [1,2,3,4] print( productExceptSelf(nums) ) nums = [-1,1,0,-3,3] print( productExceptSelf(nums) ) nums = [1, 5, 7, 2] print( productExceptSelf(nums) )
[24, 12, 8, 6] [0, 0, 9, 0, 0] [70, 14, 10, 35]
273. Integer to English Words
Convert a non-negative integer num to its English words representation.
Example 1: Input: num = 123 Output: "One Hundred Twenty Three"
Example 2: Input: num = 12345 Output: "Twelve Thousand Three Hundred Forty Five"
Example 3: Input: num = 1234567 Output: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
Example 4: Input: num = 1234567891 Output: "One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One"
Constraints: 0 <= num <= 2^31 - 1
def numberToWords(num: int)-> int: ''' Leetcode solution ''' def ones(num): switcher = { 1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five', 6: 'Six', 7: 'Seven', 8: 'Eight', 9: 'Nine' } return switcher.get(num) def teens(num): switcher = { 10: 'Ten', 11: 'Eleven', 12: 'Twelve', 13: 'Thirteen', 14: 'Fourteen', 15: 'Fifteen', 16: 'Sixteen', 17: 'Seventeen', 18: 'Eighteen', 19: 'Nineteen' } return switcher.get(num) def tens(num): switcher = { 2: 'Twenty', 3: 'Thirty', 4: 'Forty', 5: 'Fifty', 6: 'Sixty', 7: 'Seventy', 8: 'Eighty', 9: 'Ninety' } return switcher.get(num) def two(num): if not num: return '' elif num < 10: return ones(num) elif num < 20: return teens(num) else: tenner = num // 10 rest = num % 10 return tens(tenner) + ' ' + ones(rest) if rest else tens(tenner) def three(num): hundred = num // 100 rest = num % 100 if not hundred and rest: return two(rest) return ones(hundred) + ' Hundred ' + two(rest) if rest else ones(hundred) + ' Hundred' billion = num // 1000000000 million = (num - billion * 1000000000) // 1000000 thousand = (num - billion * 1000000000 - million * 1000000) // 1000 rest = num % 1000 if not num: return 'Zero' result = '' if billion: result = three(billion) + ' Billion' if million: result += ' ' if result else '' result += three(million) + ' Million' if thousand: result += ' ' if result else '' result += three(thousand) + ' Thousand' if rest: result += ' ' if result else '' result += three(rest) return result num1 = 123 num2 = 12345 num3 = 1234567 num4 = 1234567891 for num in [num1, num2, num3, num4]: print( numberToWords(num) )
One Hundred Twenty Three Twelve Thousand Three Hundred Forty Five One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One
num = 1234567890 billion = num // 1000000000 million = (num - billion * 1000000000) // 1000000 thousand = (num - billion * 1000000000 - million * 1000000) // 1000 rest = num - billion * 1000000000 - million * 1000000 - thousand * 1000 print( billion, million, thousand, rest)
1 234 567 890
283. Move Zeroes
Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements - in-place.
Example 1: Input: nums = [0,1,0,3,12] Output: [1,3,12,0,0]
Example 2: Input: nums = [0] Output: [0]
Constraints: * 1 <= nums.length <= 104 * -231 <= nums[i] <= 231 - 1
Follow up: Could you minimize the total number of operations done?
def moveZeroes(nums: List[int])-> None: ''' No Leetcode solution for Python. This is a brilliant solution from discussion ''' k = 0 # always next idx after last non-zero element for i in range(len(nums)): if nums[i] != 0: nums[k], nums[i] = nums[i], nums[k] k += 1 def moveZeroes2(nums: List[int]) -> None: ''' My accepted solution, but runs for too long (because too many elements are shifted) ''' i = 0 length = len(nums) last_idx0 = length - 1 while i < last_idx0: if nums[i] == 0: for j in range(i, length-1): nums[j] = nums[j+1] nums[-1] = 0 last_idx0 -= 1 else: i += 1 def moveZeroes1(nums: List[int]) -> None: ''' This is not in-place for some reason (only `return nums` would work) ''' length = len(nums) idxs = [] nums = [e for e in nums if e != 0] nums[:] = nums + [0]*(length - len(nums)) print(nums) nums = [0,1,0,3,12] moveZeroes( nums ) nums
[1, 3, 12, 0, 0]
340. Longest Substring with At Most K Distinct Characters
Str s, int k: return length of longest substring with at most k distinct chars
Example 1: Input: s = "eceba", k = 2 Output: 3 Explanation: The substring is "ece" with length 3.
Example 2: Input: s = "aa", k = 1 Output: 2 Explanation: The substring is "aa" with length 2.
Constraints: * 1 <= s.length <= 5 * 10^4 * 0 <= k <= 50
Algorithm * Return 0 if not str or k == 0 * Left, right pointers = 0, max_len = 1 * While right < N: * a) If s[right] in ordered dict hashmap - delete it => to ensure first key in hashmap = leftmost character. * b) Add s[right] to hashmap, move right pointer to the right * c) If len(hashmap) == k + 1 distinct chars, remove leftmost, move left pointer => sliding window has k distinct chars. * Update max_len
from collections import OrderedDict def lengthOfLongestSubstringKDistinct(s: str, k: int) -> int: n = len(s) if k == 0 or n == 0: return 0 max_len = 1 left, right = 0, 0 # sliding window pointers hashmap = OrderedDict() # hashmap for disticnt chars while right < n: # ADD RIGHTMOST char = s[right] if char in hashmap: # for char to be rightmost in hashmap del hashmap[char] hashmap[char] = right right += 1 # CHECK IF MORE THAN K DISTICNT CHARS if len(hashmap) == k + 1: _, del_idx = hashmap.popitem(last = False) left = del_idx + 1 max_len = max(max_len, right-left) return max_len s = 'eceba' k = 2 print( lengthOfLongestSubstringKDistinct(s, k) ) s = 'aa' k = 1 print( lengthOfLongestSubstringKDistinct(s, k) ) s = 'ab' k = 1 print( lengthOfLongestSubstringKDistinct(s, k) ) s = 'ababffzzeee' k = 3 print( lengthOfLongestSubstringKDistinct(s, k) )
3 2 1 7
468. Validate IP Address
Given a string IP, return "IPv4" if IP is a valid IPv4 address, "IPv6" if IP is a valid IPv6 address or "Neither" if IP is not a correct IP of any type.
A valid IPv4 address
* "x1.x2.x3.x4" where 0 <= xi <= 255
* xi cannot contain leading zeros
For example, "192.168.1.1" and "192.168.1.0" are valid IPv4 addresses "192.168.01.1", "192.168.1.00" and "192.168@1.1" are not.
A valid IPv6 address * "x1:x2:x3:x4:x5:x6:x7:x8" where 1 <= len(xi) <= 4 * xi = hexadecimal string containing digits, lower-case & upper-case ('A' to 'F'). * Leading zeros are allowed in xi.
For example, "2001:0db8:85a3:0000:0000:8a2e:0370:7334" and "2001:db8:85a3:0:0:8A2E:0370:7334" are valid IPv6 addresses "2001:0db8:85a3::8A2E:037j:7334" and "02001:0db8:85a3:0000:0000:8a2e:0370:7334" are not
Example 1: Input: IP = "172.16.254.1" Output: "IPv4" Explanation: This is a valid IPv4 address, return "IPv4".
Example 2: Input: IP = "2001:0db8:85a3:0:0:8A2E:0370:7334" Output: "IPv6" Explanation: This is a valid IPv6 address, return "IPv6".
Example 3: Input: IP = "256.256.256.256" Output: "Neither" Explanation: This is neither a IPv4 address nor a IPv6 address.
Example 4: Input: IP = "2001:0db8:85a3:0:0:8A2E:0370:7334:" Output: "Neither"
Example 5: Input: IP = "1e1.4.5.6" Output: "Neither"
Constraints: * IP consists only of English letters, digits and the characters '.' and ':'.
def validIPAddress(IP: str) -> str: ''' My accepted solution. Time c. O(N) because iterating all (at least for ipv6), space c. O(1) ''' def validate_ipv4(IP: str)->bool: if not '.' in IP: return False IP = IP.split('.') if not len(IP) == 4: return False if any([i.startswith('0') and len(i) > 1 for i in IP]): return False for i in IP: try: if int(i) < 0 or int(i) > 255: return False except: return False return True def validate_ipv6(IP: str)->bool: allowed = '0123456789abcdefABCDEF' if not ':' in IP: return False IP = IP.split(':') if not len(IP) == 8: return False for i in IP: if not i: return False if len(i) > 4: return False if any([c not in allowed for c in i]): return False return True ipv4 = validate_ipv4(IP) ipv6 = validate_ipv6(IP) if ipv4: return 'IPv4' elif ipv6: return 'IPv6' else: return 'Neither' import re def validIPAddress(IP: str) -> str: ''' Leetcode regex solution. Time c. O(1)? because patterns to match have constant length. Space c. O(1) ''' chunk_IPv4 = r'([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' patten_IPv4 = re.compile(r'^(' + chunk_IPv4 + r'\.){3}' + chunk_IPv4 + r'$') chunk_IPv6 = r'([0-9a-fA-F]{1,4})' patten_IPv6 = re.compile(r'^(' + chunk_IPv6 + r'\:){7}' + chunk_IPv6 + r'$') if patten_IPv4.match(IP): return "IPv4" return "IPv6" if patten_IPv6.match(IP) else "Neither" IPs = [ '172.16.254.1', '2001:0db8:85a3:0:0:8A2E:0370:7334', '256.256.256.256', '2001:0db8:85a3:0:0:8A2E:0370:7334:', '1e1.4.5.6', ] for IP in IPs: print( validIPAddress(IP) )
IPv4 IPv6 Neither Neither Neither
560. Subarray Sum Equals K
Array of int nums and int k - return total num continuous subarrays with sum k
Example 1: Input: nums = [1,1,1], k = 2 Output: 2
Example 2: Input: nums = [1,2,3], k = 3 Output: 2
Constraints: * 1 <= nums.length <= 2 * 10^4 * -1000 <= nums[i] <= 1000 * -10^7 <= k <= 10^7
Idea: if curr_sum up to two indices, say i and j is at a difference of k i.e. sum[i]-sum[j] = k, the sum of elements lying between indices i and j is k, too
from collections import defaultdict def subarraySum(nums: List[int], k: int) -> int: ''' Combined Leetcode and my solution. Time c. = space c. = 0 Lines that are commented out - case when you keep actual subarrays, not just their count ''' curr_sum = 0 hash_map = defaultdict() hash_map[0] = 1 count = 0 for i in range(len(nums)): curr_sum += nums[i] if curr_sum - k in hash_map: count += hash_map.get(curr_sum - k) #alist = hash_map[curr_sum - k] #for value in alist: # res.append(nums[value+1: i+1]) hash_map[curr_sum] = hash_map.get(curr_sum, 0) + 1 #hash_map[curr_sum].append(i) return count nums = [1,1,1] k = 2 print( subarraySum(nums, k) ) nums = [1,2,3] k = 3 print( subarraySum(nums, k) )
2 2
680. Valid Palindrome II
Given a string s, return true if s can be palindrome after deleting at most one char
Example 1: Input: s = "aba" Output: true
Example 2: Input: s = "abca" Output: true Explanation: You could delete the character 'c'.
Example 3: Input: s = "abc" Output: false
Constraints: * 1 <= s.length <= 10^5 * s consists of lowercase English letters
def validPalindrome(s: str) -> bool: ''' Accetped variation of the classical isPalindrome() problem where, when s[l] != s[r], you try to skip s[l] first (counting mismatches) then try to skip s[r] second (counting mismatches). If any of mismatches < 2, one option is a palindrome Time c. still O(n) ''' if not s: return False elif len(s) == 1: return True s = ''.join([c for c in s.lower() if c.isalnum()]) diff1, diff2 = 0, 0 l, r = 0, len(s) - 1 while l < r: if not s[l] == s[r]: diff1 += 1 l += 1 if diff1 > 1 and diff2 > 1: # to avoid unnecassary repetition return False if diff1 > 1: # to avoid unnecassary repetition break else: l += 1 r -= 1 l, r = 0, len(s) - 1 while l < r: if not s[l] == s[r]: diff2 += 1 r -= 1 if diff1 > 1 and diff2 > 1: # to avoid unnecassary repetition return False if diff2 > 1: # to avoid unnecassary repetition break else: l += 1 r -= 1 return (diff1 < 2) or (diff2 < 2) def validPalindrome2(s): def isPalindrome(s, start, end, delCount): ''' No Leetcode solution. This is a recursive variant of isPalindrome from Discussions ''' if delCount > 1: return False while start < end: if s[start] != s[end]: break start += 1 end -= 1 if (start == end) or (start == end+1): return True return any([isPalindrome(s, start+1, end, delCount+1), isPalindrome(s, start, end-1, delCount+1)]) return isPalindrome(s, 0, len(s)-1, 0) strs = ['aba','abca', 'abc' ] for s in strs: print( validPalindrome(s) )
0 0 True 1 1 True 2 2 False
Part II. Linked lists
2. Add Two Numbers
Two non-empty linked lists, one digit per node, represent two non-negative integers in reverse order. Add the two numbers, return sum as a reversed linked list. No leading zero, except the number 0 itself

Example 1: Input: l1 = [2,4,3], l2 = [5,6,4] Output: [7,0,8] Explanation: 342 + 465 = 807.
Example 2: Input: l1 = [0], l2 = [0] Output: [0]
Example 3: Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] Output: [8,9,9,9,0,0,0,1]
Constraints: * The number of nodes in each linked list is in the range [1, 100]. * 0 <= Node.val <= 9 * It is guaranteed that the list represents a number that does not have leading zeros EXCEPT FOR NUMBER 0 ITSELF
# t.c. = s.c. = O(max(n, m)) class Node: def __init__(self, val=0, next=None): self.val = val self.next = next def addTwoNumbers(l1: Node, l2: Node) -> Node: prehead = Node(0) # A dummy head to simplify handling the result. current = prehead # Pointer to construct the new list. carry = 0 # carry from each addition while l1 or l2 or carry: # Traverse both lists until exhausted. val1 = l1.val if l1 else 0 # If either list ran out of digits, use 0 val2 = l2.val if l2 else 0 total = val1 + val2 + carry carry = total // 10 # New carry new_digit = total % 10 current.next = Node(new_digit) current = current.next if l1: l1 = l1.next # Move to the next nodes if l2: l2 = l2.next return prehead.next # Return head of new list # Output: [7,0,8] l1 = [2,4,3], l2 = [5,6,4] n1 = Node(val=2) n2 = Node(val=4) n3 = Node(val=3) n1.next = n2 n2.next = n3 n4 = Node(val=5) n5 = Node(val=6) n6 = Node(val=4) n4.next = n5 n5.next = n6 n_new = addTwoNumbers(n1, n4) while n_new: print(n_new.val, ', ', end='') n_new = n_new.next
7 , 0 , 8 ,
21. Merge Two Sorted Lists
Merge two sorted non-decreasing linked lists, return sorted list by splicing together nodes of the first two lists
Example 1: Input: l1 = [1,2,4], l2 = [1,3,4] Output: [1,1,2,3,4,4]
Example 2: Input: l1 = [], l2 = [] Output: []
Example 3: Input: l1 = [], l2 = [0] Output: [0]
Constraints: * The number of nodes in both lists is in the range [0, 50]. * -100 <= Node.val <= 100 * Both l1 and l2 are sorted in non-decreasing order
from typing import Optional class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def mergeTwoLists(l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: ''' Leetcode solution Time c. O(n+m), space c. O(1) ''' # reference to return node (prehead will be disregarded as it's a singly linked list + head is returned) prehead = ListNode(0) current = prehead while l1 and l2: if l1.val <= l2.val: current.next = l1 # assign entire node, NOT ListNode( l1.val )! l1 = l1.next else: current.next = l2 l2 = l2.next current = current.next # l1 or l2 can still have nodes => attach remainder without iteration! current.next = l1 if l1 else l2 return prehead.next l1 = [1,2,4] l2 = [1,3,4] n1 = Node(val=1) n2 = Node(val=2) n3 = Node(val=4) n1.next = n2 n2.next = n3 n4 = Node(val=1) n5 = Node(val=3) n6 = Node(val=4) n4.next = n5 n5.next = n6 n_new = mergeTwoLists(n1, n4) n_new
<__main__.Node at 0x1101748d0>
while n_new: print(n_new.val, end=' ') n_new = n_new.next
1 1 2 3 4 4
138. Copy List with Random Pointer (Facebook)
A linked list of length n is given such that each node contains an additional random pointer, which could point to any node in the list, or null.
Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state. None of the pointers in the new list should point to nodes in the original list.
For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding two nodes x and y in the copied list, x.random --> y.
Return the head of the copied linked list.
The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:
val: an integer representing Node.val random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any node.
Your code will only be given the head of the original linked list
from collections import defaultdict class Node: def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): self.val = int(x) self.next = next self.random = random def copyRandomList(head: 'Node') -> 'Node': # Nodes as keys, their copies as values node_map = defaultdict(lambda: None) # Fill node_map curr = head while curr: node_map[curr] = Node(curr.val) curr = curr.next # Update next and random pointer of the copied nodes curr = head while curr: dup = node_map[curr] dup.next = node_map[curr.next] dup.random = node_map[curr.random] curr = curr.next return node_map[head] #head = [[3,null],[3,0],[3,null]] node_0 = Node(3) node_1 = Node(3) node_2 = Node(3) node_0.next = node_1 node_1.next = node_2 node_1.random = node_0 copyRandomList(node_0)
<__main__.Node at 0x7fbbc1442990>
143. Reorder List
Given the head of singly linked-list, reorder it: L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … Cannot modify values in list's nodes, only nodes themselves may be changed.
Example 1: Input: head = [1,2,3,4] Output: [1,4,2,3]
Example 2: Input: head = [1,2,3,4,5] Output: [1,5,2,4,3]
Constraints: * Number nodes in list: [1, 5 * 104] * 1 <= Node.val <= 1000
This problem is a combination of three easy problems!
- Middle of the Linked List (#876)
- Reverse Linked List (#206)
- Merge Two Sorted Lists (#21)
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def reorderList(head: Optional[ListNode]) -> None: ''' Do not return anything, modify head in-place instead Time c. O(n), space c. O(1) ''' if not head: return # find middle [Problem 876] slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next # reverse second part of list in-place [Problem 206] prev, curr = None, slow while curr: curr.next, prev, curr = prev, curr, curr.next # merge two sorted linked lists [Problem 21] # merge 1->2->3->4 and 6->5->4 into 1->6->2->5->3->4 first, second = head, prev while second.next: first.next, first = second, first.next second.next, second = first, second.next l1 = [1,2,3,4] n1 = ListNode(val=1) n2 = ListNode(val=2) n3 = ListNode(val=3) n4 = ListNode(val=4) n1.next = n2 n2.next = n3 n3.next = n4 head0 = n1 reorderList(n1) curr0 = head0 while curr0: print(curr0.val, end=' ') curr0 = curr0.next
1 4 2 3
Part III. Trees and Graphs
Facebook loves Tree types of problems. Favorite one: convert BST to Sorted Doubly Linked List (below). You should also know topological sort and graph traversal (still asked occasionally)
938. Range Sum of BST
Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high]
* Input: root = [10,5,15,3,7,null,18], low = 7, high = 15
* Output: 32
* Explanation: Nodes 7, 10, and 15 are in the range [7, 15]. 7 + 10 + 15 = 32.

My submission * Runtime: 216 ms, faster than 63.90% of Python3 online submissions for Range Sum of BST. * Memory Usage: 22.2 MB, less than 84.79% of Python3 online submissions for Range Sum of BST
from typing import Optional, Union, List class Node: def __init__( self, val: int=0, left: Optional[int]=None, right: Optional[int]=None, ) -> None: self.val=val self.left=left self.right=right def range_sum( root: Optional[TreeNode], low: int, high: int, ) -> int: if root is None: return 0 res = 0 if root.val < low: res += range_sum(root.right, low, high) elif root.val > high: res += range_sum(root.left, low, high) else: res += root.val + range_sum(root.left, low, high) + range_sum(root.right, low, high) return res
low = 7 high = 15 root = TreeNode(10) root.left = TreeNode(5) root.left.left = TreeNode(3) root.left.right = TreeNode(7) root.right = TreeNode(15) root.right.right = TreeNode(18) rangeSumBST(root, low, high)
32
426. Convert Binary Search Tree to Sorted Circular Doubly Linked List
Convert a Binary Search Tree to a sorted Circular Doubly-Linked List in place.
You can think of the left and right pointers as synonymous to the predecessor and successor pointers in a doubly-linked list. For a circular doubly linked list, the predecessor of the first element is the last element, and the successor of the last element is the first element.
We want to do the transformation in place. After the transformation, the left pointer of the tree node should point to its predecessor, and the right pointer should point to its successor. You should return the pointer to the smallest element of the linked list.
Example 1: Input: root = [4,2,5,1,3] Output: [1,2,3,4,5] Explanation: The figure below shows the transformed BST. The solid line indicates the successor relationship, while the dashed line means the predecessor relationship.
Example 2: Input: root = [2,1,3] Output: [1,2,3]
Example 3: Input: root = [] Output: [] Explanation: Input is an empty tree. Output is also an empty Linked List.
Example 4: Input: root = [1] Output: [1]
Constraints: * The number of nodes in the tree is in the range [0, 2000] * -1000 <= Node.val <= 1000 * All the values of the tree are unique
Solution * Inorder recursion: left -> node -> right * Left & right parts are recursion calls * Node part is where all processing is done
Complexity * Time c. O(N) - each node is processed exactly once. * Space c. O(N) - keeping recursion stack of the size of the tree height, O(logN) for the best case of completely balanced tree and O(N) for the worst case of completely unbalanced tree
''' # Definition for a Node. class Node: def __init__(self, val, left=None, right=None): self.val = val self.left = left self.right = right ''' class Solution: def treeToDoublyList(self, root: 'Node') -> 'Node': def helper(node): ''' Standard inorder traversal: left -> node -> right, linking all nodes into DLL ''' nonlocal last, first if node: helper(node.left) # left if last: # node last.right = node # link prev node (last) with node node.left = last else: first = node # memorize first smallest node last = node helper(node.right) # right if not root: return None first, last = None, None # smallest (first) & largest (last) nodes helper(root) last.right = first # close DLL first.left = last return first
114. Flatten Binary Tree to Linked List
Given the root of a binary tree, flatten the tree into a "linked list": * Use same TreeNode class, right child pointer => next node, left child = always null. * Linked list's order - pre-order traversal of BT (root-L-R)
Example 1: Input: root = [1,2,5,3,4,null,6] Output: [1,null,2,null,3,null,4,null,5,null,6]
Example 2: Input: root = [] Output: []
Example 3: Input: root = [0] Output: [0]
Constraints: * The number of nodes in the tree is in the range [0, 2000]. * -100 <= Node.val <= 100
Follow up: can you flatten the tree in-place (with O(1) extra space)?
Solution 1 (Recursive, O(n), O(n))
# class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None class Solution: def flattenTree(self, node): if not node: # null scenario return None if not node.left and not node.right: # if leaf node, return node return node left = self.flattenTree(node.left) # flatten left half recursively right = self.flattenTree(node.right) # flatten right half recursively if left: # If left subtree, modify connections => nothing on left left.right = node.right node.right = node.left node.left = None return rightTail if rightTail else leftTail # once done re-wiring, return "rightmost" node def flatten(self, root: TreeNode) -> None: ''' Do not return anything, modify root in-place instead. ''' self.flattenTree(root)
Solution 2 (Iterative, O(n), O(1))


class Solution: def flatten(self, root: TreeNode) -> None: ''' Do not return anything, modify root in-place instead ''' if not root: # null scenario return None node = root while node: if node.left: # if left child rightmost = node.left # find the rightmost node while rightmost.right: rightmost = rightmost.right rightmost.right = node.right # rewire connections node.right = node.left node.left = None node = node.right # move on to right side of tree
Trees and Graphs
98. Validate Binary Search Tree
Given the root of a binary tree, determine if it is a valid binary search tree (BST).
A valid BST is defined as follows:
* The left subtree of a node contains only nodes with keys less than the node's key.
* The right subtree of a node contains only nodes with keys greater than the node's key.
* Both the left and right subtrees must also be binary search trees.
Example 1: Input: root = [2,1,3] Output: true
Example 2: Input: root = [5,1,4,null,null,3,6] Output: false Explanation: The root node's value is 5 but its right child's value is 4.
Constraints: * Num of nodes in the range [1, 104]. * -2^31 <= Node.val <= 2^31 - 1
Solution: * Check if each element in inorder is smaller than the next one
import math class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def isValidBST(self, root: TreeNode) -> bool: ''' Iterative. O(n), O(n) ''' stack, prev = [], -math.inf while stack or root: while root: stack.append(root) root = root.left # If next elem in inorder traversal < prev elem => not BST root = stack.pop() if root.val <= prev: return False prev = root.val root = root.right return True
124. Binary Tree Maximum Path Sum
Path in binary tree - sequence of adjacent nodes (connected with edges). A node appears in seq only once. Path does not need to pass through root. Sum path = sum of node's values.
Given root of binary tree, return any max sum path
Example 1: Input: root = [1,2,3] Output: 6 Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.
Example 2: Input: root = [-10,9,20,null,null,15,7] Output: 42 Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.
Constraints: * The number of nodes in the tree is in the range [1, 3 * 10^4]. * -1000 <= Node.val <= 1000
# Definition for a binary tree node. class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right class Solution: def maxPathSum(self, root: Optional[TreeNode]) -> int: def max_gain(node): nonlocal max_sum if not node: return 0 left_sum = max( max_gain(node.left), 0 ) # max sum for left and right sub-trees right_sum = max( max_gain(node.right), 0 ) # 0 - because neg nums decrease sum newpath_sum = node.val + left_sum + right_sum # gain for new path with `node` at top max_sum = max(max_sum, newpath_sum) # update max_sum return node.val + max(left_sum, right_sum) # for recursion max_sum = float('-inf') max_gain( root ) return max_sum
133. Clone Graph
Given reference of a node, always the first node with val = 1, in a connected undirected graph, return a deep copy (clone) of the graph. Each node has a value (int) and a an list of neighbors.
Example 1: Input: adjList = [[2,4],[1,3],[2,4],[1,3]] Output: [[2,4],[1,3],[2,4],[1,3]] Explanation: There are 4 nodes in the graph. 1st node (val = 1)'s neighbors are 2nd node (val = 2) and 4th node (val = 4) 2nd node (val = 2)'s neighbors are 1st node (val = 1) and 3rd node (val = 3) 3rd node (val = 3)'s neighbors are 2nd node (val = 2) and 4th node (val = 4) 4th node (val = 4)'s neighbors are 1st node (val = 1) and 3rd node (val = 3)
Example 2: Input: adjList = [[]] Output: [[]] Explanation: Note that the input contains one empty list. The graph consists of only one node with val = 1 and it does not have any neighbors.
Example 3: Input: adjList = [] Output: [] Explanation: This an empty graph, it does not have any nodes.
Example 4: Input: adjList = [[2],[1]] Output: [[2],[1]]
Constraints: * The number of nodes in the graph is in the range [0, 100]. * 1 <= Node.val <= 100 * Node.val is unique for each node. * There are no repeated edges and no self-loops in the graph. * The Graph is connected and all nodes can be visited starting from the given node.
Solution: DFS or BFS when visited is actually a dict[curr_node] = cloned_node
# Definition for a Node. class Node: def __init__(self, val=0, neighbors=None): self.val = val self.neighbors = neighbors if neighbors is not None else [] class Solution: def cloneGraph(self, start: 'Node') -> 'Node': if not node: return node visited, queue = {}, [start] # Dict[visited node] = its clone, to avoid cycles visited[start] = Node(start.val, []) # Clone it, put into visited while queue: vertex = queue.pop(0) # get node for neighbor in vertex.neighbors: # Iterate neighbors if neighbor not in visited: visited[neighbor] = Node(neighbor.val, []) # Clone them, put into visited queue.append(neighbor) visited[n].neighbors.append(visited[neighbor]) # Add clone of neighbor to clone's neighbors return visited[node]
199. Binary Tree Right Side View
BT, you stand on the right side, return values of nodes you can see ordered from top to bottom
Example 1: Input: root = [1,2,3,null,5,null,4] Output: [1,3,4]
Example 2: Input: root = [1,null,3] Output: [1,3]
Example 3: Input: root = [] Output: []
Constraints: * The number of nodes in the tree is in the range [0, 100]. * -100 <= Node.val <= 100
One out of 4 provided solutions - recursive DFS: * Time c.: O(N) - have to visit each node * Space c.: O(H) - recursion stack (H = tree height). Worst-case - skewed tree, when H=N
# Definition for a binary tree node class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right class Solution: def rightSideView(self, root: TreeNode) -> List[int]: if root is None: return [] rightside = [] def helper(node: TreeNode, level: int) -> None: if level == len(rightside): rightside.append(node.val) for child in [node.right, node.left]: if child: helper(child, level + 1) helper( root, 0 ) return rightside
200. Number of Islands
Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1: Input: grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] Output: 1
Example 2: Input: grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] Output: 3
Constraints: * m == grid.length * n == grid[i].length * 1 <= m, n <= 300 * grid[i][j] is '0' or '1'
def numIslands(grid): if not grid: return 0 count = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == '1': dfs(grid, i, j) count += 1 return count def dfs(grid, i, j): if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j] != '1': return grid[i][j] = '#' # can be anything, but 1 dfs(grid, i+1, j) dfs(grid, i-1, j) dfs(grid, i, j+1) dfs(grid, i, j-1)
grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"], ] print(numIslands(grid))
3
236. Lowest Common Ancestor of a Binary Tree
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes. Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”
Example 1: Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 Output: 3 Explanation: The LCA of nodes 5 and 1 is 3.
Example 2: Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 Output: 5 Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.
Example 3: Input: root = [1,2], p = 1, q = 2 Output: 1
Constraints: * The number of nodes in the tree is in the range [2, 10^5]. * -10^9 <= Node.val <= 10^9 * All Node.val are unique. * p != q * p and q will exist in the tree.
Below is a solution from my notebook (accepted by LeetCode; simpler than any of the 4 LeetCode solutions: * Time c. O(N) - visiting all N nodes in the worst case * Space c. O(N) - skewed binary tree with height N => parent pointer dictionary and the ancestor set would be N long each
# Definition for a binary tree node class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None def lca(root, p, q): """ SIMPLER THAN ANY LEETCODE SOLUTION :type root: TreeNode :type p: TreeNode :type q: TreeNode :rtype: TreeNode """ if not root or root is p or root is q: # base case return root left = lca(root.left, p, q) right = lca(root.right, p, q) if left and right: # this is lca return root return left if left else right # p,q are both on one side of the tree
257. Binary Tree Paths
Given the root of a binary tree, return all root-to-leaf paths in any order. Leaf = node with no children
Example 1: Input: root = [1,2,3,null,5] Output: ["1->2->5","1->3"]
Example 2: Input: root = [1] Output: ["1"]
Constraints: * The number of nodes in the tree is in the range [1, 100]. * -100 <= Node.val <= 100
Complexity: * Time c. O(N) - each node visited once. * Space c. O(N) - we could keep up to the entire tree
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def binaryTreePaths(self, root): ''' Recursive solution ''' def get_path(root, path): if root: path += str(root.val) if not root.left and not root.right: # reached leaf paths.append(path) else: path += '->' get_path(root.left, path) get_path(root.right, path) paths = [] get_path(root, '') return paths def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]: ''' Iterative slution ''' if not root: return [] paths = [] stack = [(root, str(root.val))] while stack: node, path = stack.pop() if not node.left and not node.right: paths.append(path) if node.left: stack.append((node.left, path + '->' + str(node.left.val))) if node.right: stack.append((node.right, path + '->' + str(node.right.val))) return paths
269. Alien Dictionary (FB interview per LeetCode's Discussions)
- A new alien language uses the English alphabet, but order among letters is unknown
- You are given a list of strings - words from the alien language's dictionary. Words are sorted lexicographically by the rules of the new language
- Return a string of unique letters sorted in lexicographically increasing order per new language. No solution - return "". Multiple solutions - return any.
- Str s is lexicographically smaller than str t if at first letter where they differ, letter in s comes before letter in t. If first min(s.length, t.length) letters are same => s is smaller iff s.length < t.length.
Example 1:
Input: words = ["wrt","wrf","er","ett","rftt"]
Output: "wertf"
Example 2:
Input: words = ["z","x"]
Output: "zx"
Example 3:
Input: words = ["z","x","z"]
Output: ""
Explanation: The order is invalid, so return "".
Constraints: * 1 <= words.length <= 100 * 1 <= words[i].length <= 100 * words[i] consists of only lowercase English letters.
Solution
1) BFS
a) get dependencies for each letter in the form of a graph's adjacency list,
b) topological sort
Complexity * Time c. O(C) * N - # strings * C - total length of all words in input list * U - total num unique letters in alphabet * There were three parts to the algorithm; identifying all the relations, putting them into an adjacency list, and then converting it into a valid alphabet ordering. * In the worst case, the first and second parts require checking every letter of every word (if the difference between two words was always in the last letter) - O(C) * For the third part, recall that a breadth-first search has a cost of O(V+E), V = num vertices and E = num edges * Space c. O(1) or O(U + \min(U^2, N))O(U+min(U
def alienOrder(self, words: List[str]) -> str: adj_list = defaultdict(set) # adj_list for each letter in_degree = Counter({c : 0 for word in words for c in word}) # in_degree of each unique letter for first_word, second_word in zip(words, words[1:]): # populate both for pairs adjacent words for c, d in zip(first_word, second_word): if c != d: if d not in adj_list[c]: adj_list[c].add(d) in_degree[d] += 1 break else: # if one word is followed by its prefix - if len(second_word) < len(first_word): return '' # can't find ordering for entire list! output = [] queue = deque([c for c in in_degree if in_degree[c] == 0]) while queue: # toposort c = queue.popleft() output.append(c) for d in adj_list[c]: in_degree[d] -= 1 if in_degree[d] == 0: queue.append(d) if len(output) < len(in_degree): # if not all letters in output - cycle => no valid ordering return '' return ''.join(output)
543. Diameter of Binary Tree
Return the length of the diameter of a binary tree = length of the longest path between any two nodes (may or may not pass through root). Length of a path = num edges
Example 1: Input: root = [1,2,3,4,5] Output: 3 Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].
Example 2: Input: root = [1,2] Output: 1
Constraints: * The number of nodes in the tree is in the range [1, 10^4]. * -100 <= Node.val <= 100
Complexity * Time c. O(N). This is because in our recursion function longestPath, we only enter and exit from each node once. We know this because each node is entered from its parent, and in a tree, nodes only have one parent. * Space c. O(N). The space complexity depends on the size of our implicit call stack during our DFS, which relates to the height of the tree. In the worst case, the tree is skewed so the height of the tree is O(N)O(N). If the tree is balanced, it'd be O(\log N)O(logN).
class Solution: def diameterOfBinaryTree(self, root: TreeNode) -> int: def longest_path(node): if not node: return 0 nonlocal diameter left_path = longest_path(node.left) # longest path in left & right child right_path = longest_path(node.right) diameter = max(diameter, left_path + right_path) # update if left_path + right_path > diam. return max(left_path, right_path) + 1 # add 1 for connection to parent diameter = 0 longest_path(root) return diameter
721. Accounts Merge
Given a list of accounts where each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account.
Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some common email to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.
After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.
Example 1: Input: accounts = [["John","johnsmith@mail.com","john_newyork@mail.com"],["John","johnsmith@mail.com","john00@mail.com"],["Mary","mary@mail.com"],["John","johnnybravo@mail.com"]] Output: [["John","john00@mail.com","john_newyork@mail.com","johnsmith@mail.com"],["Mary","mary@mail.com"],["John","johnnybravo@mail.com"]] Explanation: The first and second John's are the same person as they have the common email "johnsmith@mail.com". The third John and Mary are different people as none of their email addresses are used by other accounts. We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], ['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted.
Example 2: Input: accounts = [["Gabe","Gabe0@m.co","Gabe3@m.co","Gabe1@m.co"],["Kevin","Kevin3@m.co","Kevin5@m.co","Kevin0@m.co"],["Ethan","Ethan5@m.co","Ethan4@m.co","Ethan0@m.co"],["Hanzo","Hanzo3@m.co","Hanzo1@m.co","Hanzo0@m.co"],["Fern","Fern5@m.co","Fern1@m.co","Fern0@m.co"]] Output: [["Ethan","Ethan0@m.co","Ethan4@m.co","Ethan5@m.co"],["Gabe","Gabe0@m.co","Gabe1@m.co","Gabe3@m.co"],["Hanzo","Hanzo0@m.co","Hanzo1@m.co","Hanzo3@m.co"],["Kevin","Kevin0@m.co","Kevin3@m.co","Kevin5@m.co"],["Fern","Fern0@m.co","Fern1@m.co","Fern5@m.co"]]
Constraints: * 1 <= accounts.length <= 1000 * 2 <= accounts[i].length <= 10 * 1 <= accounts[i][j] <= 30 * accounts[i][0] consists of English letters * accounts[i][j] (for j > 0) is a valid email
Solution: For each account, draw edge from 1st email to other emails. Also, create 'emails to names' map. Then DFS and add each connected component to answer
Complexity: * Time c. O(\sum a_i \log a_i), where a_i is the length of accounts[i]. Without the log factor, this is the complexity to build the graph and search for each component. The log factor is for sorting each component at the end. * Space c. O(\sum a_i) - the space used by our graph and our search
class Solution(object): def accountsMerge(self, accounts): em_to_name = {} graph = collections.defaultdict(set) for acc in accounts: name = acc[0] for email in acc[1:]: graph[acc[1]].add(email) graph[email].add(acc[1]) em_to_name[email] = name seen = set() ans = [] for email in graph: if email not in seen: seen.add(email) stack = [email] component = [] while stack: node = stack.pop() component.append(node) for nei in graph[node]: if nei not in seen: seen.add(nei) stack.append(nei) ans.append([em_to_name[email]] + sorted(component)) return ans
LeetCode Facebook problems not reviewed
785. Is Graph Bipartite?
There is an undirected graph with n nodes, where each node is numbered between 0 and n - 1. You are given a 2D array graph, where graph[u] is an array of nodes that node u is adjacent to. More formally, for each v in graph[u], there is an undirected edge between node u and node v. The graph has the following properties:
There are no self-edges (graph[u] does not contain u). There are no parallel edges (graph[u] does not contain duplicate values). If v is in graph[u], then u is in graph[v] (the graph is undirected). The graph may not be connected, meaning there may be two nodes u and v such that there is no path between them. A graph is bipartite if the nodes can be partitioned into two independent sets A and B such that every edge in the graph connects a node in set A and a node in set B.
Return true if and only if it is bipartite.
Example 1:
Input: graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
Output: false
Explanation: There is no way to partition the nodes into two independent sets such that every edge connects a node in one and a node in the other.
Example 2:
Input: graph = [[1,3],[0,2],[1,3],[0,2]]
Output: true
Explanation: We can partition the nodes into two sets: {0, 2} and {1, 3}.
Constraints: * graph.length == n * 1 <= n <= 100 * 0 <= graph[u].length < n * 0 <= graph[u][i] <= n - 1 * graph[u] does not contain u. * All the values of graph[u] are unique. * If graph[u] contains v, then graph[v] contains u.
Time c.: * O(N+E), where N is the number of nodes in the graph, and E is the number of edges. We explore each node once when we transform it from uncolored to colored, traversing all its edges in the process * Space Complexity: O(N)O(N), the space used to store the color
class Solution(object): def isBipartite(self, graph): color = {} for node in xrange(len(graph)): if node not in color: stack = [node] color[node] = 0 while stack: node = stack.pop() for nei in graph[node]: if nei not in color: stack.append(nei) color[nei] = color[node] ^ 1 elif color[nei] == color[node]: return False return True
314. Binary Tree Vertical Order Traversal
Given the root of a binary tree, return the vertical order traversal of its nodes' values. (i.e., from top to bottom, column by column). If two nodes are in the same row and column, the order should be from left to right.
Example 1:
Input: root = [3,9,20,null,null,15,7]
Output: [[9],[3,15],[20],[7]]
Example 2:
Input: root = [3,9,8,4,0,1,7]
Output: [[4],[9],[3,0,1],[8],[7]]
Example 3:
Input: root = [3,9,8,4,0,1,7,null,null,null,2,5]
Output: [[4],[9,5],[3,0,1],[8,2],[7]]
Example 4: Input: root = [] Output: []
Constraints: * The number of nodes in the tree is in the range [0, 100]. * -100 <= Node.val <= 100
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None from collections import defaultdict class Solution: def verticalOrder(self, root: TreeNode) -> List[List[int]]: ''' DFS Time c. O(NlogN) where NN is the number of nodes in the tree Space c. O(N) ''' columnTable = defaultdict(list) queue = deque([(root, 0)]) while queue: node, column = queue.popleft() if node is not None: columnTable[column].append(node.val) queue.append((node.left, column - 1)) queue.append((node.right, column + 1)) return [columnTable[x] for x in sorted(columnTable.keys())]
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None from collections import defaultdict class Solution: def verticalOrder(self, root: TreeNode) -> List[List[int]]: ''' BFS Time c. O(N) Space c. O(N) ''' if root is None: return [] columnTable = defaultdict(list) min_column = max_column = 0 queue = deque([(root, 0)]) while queue: node, column = queue.popleft() if node is not None: columnTable[column].append(node.val) min_column = min(min_column, column) max_column = max(max_column, column) queue.append((node.left, column - 1)) queue.append((node.right, column + 1)) return [columnTable[x] for x in range(min_column, max_column + 1)]
317. Shortest Distance from All Buildings
You are given an m x n grid grid of values 0, 1, or 2, where: each 0 marks an empty land that you can pass by freely, each 1 marks a building that you cannot pass through, and each 2 marks an obstacle that you cannot pass through. You want to build a house on an empty land that reaches all buildings in the shortest total travel distance. You can only move up, down, left, and right.
Return the shortest travel distance for such a house. If it is not possible to build such a house according to the above rules, return -1.
The total travel distance is the sum of the distances between the houses of the friends and the meeting point.
The distance is calculated using Manhattan Distance, where distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|.
Example 1: Input: grid = [[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]] Output: 7 Explanation: Given three buildings at (0,0), (0,4), (2,2), and an obstacle at (0,2). The point (1,2) is an ideal empty land to build a house, as the total travel distance of 3+3+1=7 is minimal. So return 7.
Example 2: Input: grid = [[1,0]] Output: 1
Example 3: Input: grid = [[1]] Output: -1
Constraints: * m == grid.length * n == grid[i].length * 1 <= m, n <= 50 * grid[i][j] is either 0, 1, or 2. * There will be at least one building in the grid.
class Solution: def shortestDistance(self, grid: List[List[int]]) -> int: rows = len(grid) cols = len(grid[0]) dirs = [(0, 1), (1, 0), (-1, 0), (0, -1)] total_sum = [[0] * cols for _ in range(rows)] def bfs(row, col, curr_count): min_distance = math.inf queue = deque() queue.append([row, col, 0]) while queue: curr_row, curr_col, curr_step = queue.popleft() for d in dirs: next_row = curr_row + d[0] next_col = curr_col + d[1] if 0 <= next_row < rows and 0 <= next_col < cols and grid[next_row][next_col] == -curr_count: total_sum[next_row][next_col] += curr_step + 1 min_distance = min(min_distance, total_sum[next_row][next_col]) grid[next_row][next_col] -= 1 queue.append([next_row, next_col, curr_step + 1]) return min_distance count = 0 for row in range(rows): for col in range(cols): if grid[row][col] == 1: min_distance = bfs(row, col, count) count += 1 if min_distance == math.inf: return -1 return min_distance
67. Add Binary
Two binary str a and b, return their sum as a binary string.
Example 1: Input: a = "11", b = "1" Output: "100"
Example 2: Input: a = "1010", b = "1011" Output: "10101"
Constraints: * 1 <= a.length, b.length <= 104 * a and b consist only of '0' or '1' characters. * Each string does not contain leading zeros except for the zero itself

def addBinary(a: str, b: str) -> str: ''' O(N+M) Low performance if large input numbers ''' return '{0:b}'.format(int(a, 2) + int(b, 2)) def addBinary2(a, b) -> str: ''' Time c. O(N+M) Space c. O(max(N,M)) Read approach 2 above - bitwise operations x stores the answer, y stores the carry ''' x, y = int(a, 2), int(b, 2) while y: # shorter: x, y = x ^ y, (x & y) << 1 answer = x ^ y carry = (x & y) << 1 x, y = answer, carry return bin(x)[2:] # returns binary string of int which starts with '0b' (to avoid) a = "11" b = "1" print( addBinary2(a,b) ) a = "1010" b = "1011" print( addBinary2(a,b) )
100 10101
bin(10)
'0b1010'
# USING CARRY AND ADDITION - better time. c, but is not an answer to a question "Do it without summing up" def addBinary(a, b) -> str: ''' Time c. O(max(N,M)), N & M =m len of a & b Space c. O(max(N,M)) to keep the answer ''' n = max(len(a), len(b)) a, b = a.zfill(n), b.zfill(n) carry = 0 answer = [] for i in range(n - 1, -1, -1): if a[i] == '1': carry += 1 if b[i] == '1': carry += 1 if carry % 2 == 1: answer.append('1') else: answer.append('0') carry //= 2 if carry == 1: answer.append('1') answer.reverse() return ''.join(answer)
31. Next Permutation
Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers. If such an arrangement is not possible, it must rearrange it as the lowest possible order (i.e., sorted in ascending order). The replacement must be in place and use only constant extra memory.
Example 1: Input: nums = [1,2,3] Output: [1,3,2]
Example 2: Input: nums = [3,2,1] Output: [1,2,3]
Example 3: Input: nums = [1,1,5] Output: [1,5,1]
Example 4: Input: nums = [1] Output: [1]
Constraints: * 1 <= nums.length <= 100 * 0 <= nums[i] <= 100


Another explanation (condensed mathematical description):
* Find largest index i such that array[i − 1] < array[i]. (If no such i exists, then this is already the last permutation.)
* Find largest index j such that j ≥ i and array[j] > array[i − 1].
* Swap array[j] and array[i − 1].
* Reverse the suffix starting at array[i].
Source: https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
def nextPermutation(nums: List[int]) -> None: ''' Runtime: 32 ms, faster than 98.61% of Python3 online submissions for Next Permutation Memory Usage: 14.3 MB, less than 19.08% of Python3 submissions - IN PLACE, WHAT MEMORY USAGE? ''' i = len(nums)-2 while i >= 0 and nums[i+1] <= nums[i]: # find first nums[i] from right that's > than its right neighbor i -= 1 if i >= 0: j = len(nums)-1 # find first nums[j] from right that's > than nums[i] while nums[j] <= nums[i]: j -= 1 nums[i], nums[j] = nums[j], nums[i] # swap them reverse(nums, i+1) # all nums to right of nums[i] are in non-incresing oder - # reverse them for next lexicographical permutation def reverse( nums: List[int], start: int) -> None: i, j = start, len(nums)-1 while i < j: nums[i], nums[j] = nums[j], nums[i] i += 1 j -= 1 a1 = [1,2,3] a2 = [3,2,1] a3 = [1,1,5] a4 = [1] for a in [a1, a2, a3, a4]: nextPermutation(a) print(a)
[1, 3, 2] [1, 2, 3] [1, 5, 1] [1]
Part VI. Recursion
46. Permutations
Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.
Example 1: Input: nums = [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Example 2: Input: nums = [0,1] Output: [[0,1],[1,0]]
Example 3: Input: nums = [1] Output: [[1]]
Constraints:
1 <= nums.length <= 6 -10 <= nums[i] <= 10 All the integers of nums are unique
Algorithm
Backtracking is an algorithm for finding all solutions by exploring all potential candidates. If the solution candidate turns to be not a solution (or at least not the last one), backtracking algorithm discards it by making some changes on the previous step, i.e. backtracks and then try again.
Here is a backtrack function which takes the index of the first integer to consider as an argument backtrack(first).
If the first integer to consider has index n that means that the current permutation is done. Iterate over the integers from index first to index n - 1. Place i-th integer first in the permutation, i.e. swap(nums[first], nums[i]). Proceed to create all permutations which starts from i-th integer : backtrack(first + 1). Now backtrack, i.e. swap(nums[first], nums[i]) back
# time = O(∑(N,k=1) P(N,k)); where P(N,k)= N! / (N−k)! = N(N−1)...(N−k+1); O(N!) class Solution: def permute(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ def backtrack(first = 0): # if all integers are used up if first == n: output.append(nums[:]) for i in range(first, n): # place i-th integer first # in the current permutation nums[first], nums[i] = nums[i], nums[first] # use next integers to complete the permutations backtrack(first + 1) # backtrack nums[first], nums[i] = nums[i], nums[first] n = len(nums) output = [] backtrack() return output
47. Permutations II
Given a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order.
Example 1: Input: nums = [1,1,2] Output: [[1,1,2], [1,2,1], [2,1,1]]
Example 2: Input: nums = [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Constraints:
1 <= nums.length <= 8 -10 <= nums[i] <= 10
Algorithm
Given the above insight, in order to find out all the unique numbers at each stage, we can build a hash table (denoted as counter), with each unique number as the key and its occurrence as the corresponding value.
To implement the algorithm, first we define a function called backtrack(comb, counter) which generates all permutations, starting from the current combination (comb) and the remaining numbers (counter).
Once the function is implemented, it suffices to invoke the function with the initial empty combination and the hash table we built out of the input array, to solve the problem
# time = O(∑(N,k=1) P(N,k)); where P(N,k)= N! / (N−k)! = N(N−1)...(N−k+1); O(N!) class Solution: def permuteUnique(self, nums: List[int]) -> List[List[int]]: results = [] def backtrack(comb, counter): if len(comb) == len(nums): # make a deep copy of the resulting permutation, # since the permutation would be backtracked later. results.append(list(comb)) return for num in counter: if counter[num] > 0: # add this number into the current combination comb.append(num) counter[num] -= 1 # continue the exploration backtrack(comb, counter) # revert the choice for the next exploration comb.pop() counter[num] += 1 backtrack([], Counter(nums)) return results
301. Remove Invalid Parentheses
Given a string s that contains parentheses and letters, remove the minimum number of invalid parentheses to make the input string valid.
Return all the possible results. You may return the answer in any order.
Example 1: Input: s = "()())()" Output: ["(())()","()()()"]
Example 2: Input: s = "(a)())()" Output: ["(a())()","(a)()()"]
Example 3: Input: s = ")(" Output: [""]
Constraints:
1 <= s.length <= 25 s consists of lowercase English letters and parentheses '(' and ')'. There will be at most 20 parentheses in s
Algorithm
The overall algorithm remains exactly the same as before. The changes that we will incorporate are listed below:
The state of the recursion is now defined by five different variables: index which represents the current character that we have to process in the original string. left_count which represents the number of left parentheses that have been added to the expression we are building. right_count which represents the number of right parentheses that have been added to the expression we are building. left_rem is the number of left parentheses that remain to be removed. right_rem represents the number of right parentheses that remain to be removed. Overall, for the final expression to be valid, left_rem == 0 and right_rem == 0. When we decide to not consider a parenthesis i.e. delete a parenthesis, be it a left or a right parentheses, we have to consider their corresponding remaining counts as well. This means that we can only discard a left parentheses if left_rem > 0 and similarly for the right one we will check for right_rem > 0. There are no changes to checks for considering a parenthesis. Only the conditions change for discarding a parenthesis. Condition for an expression being valid in the base case would now become left_rem == 0 and right_rem == 0. Note that we don't have to check if left_count == right_count anymore because in the case of a valid expression, we would have removed all the misplaced or invalid parenthesis by the time the recursion ends. So, the only check we need if left_rem == 0 and right_rem == 0.
# time = O(2^N), space = O(N) class Solution: def removeInvalidParentheses(self, s): """ :type s: str :rtype: List[str] """ left = 0 right = 0 # First, we find out the number of misplaced left and right parentheses. for char in s: # Simply record the left one. if char == '(': left += 1 elif char == ')': # If we don't have a matching left, then this is a misplaced right, record it. right = right + 1 if left == 0 else right # Decrement count of left parentheses because we have found a right # which CAN be a matching one for a left. left = left - 1 if left > 0 else left result = {} def recurse(s, index, left_count, right_count, left_rem, right_rem, expr): # If we reached the end of the string, just check if the resulting expression is # valid or not and also if we have removed the total number of left and right # parentheses that we should have removed. if index == len(s): if left_rem == 0 and right_rem == 0: ans = "".join(expr) result[ans] = 1 else: # The discard case. Note that here we have our pruning condition. # We don't recurse if the remaining count for that parenthesis is == 0. if (s[index] == '(' and left_rem > 0) or (s[index] == ')' and right_rem > 0): recurse(s, index + 1, left_count, right_count, left_rem - (s[index] == '('), right_rem - (s[index] == ')'), expr) expr.append(s[index]) # Simply recurse one step further if the current character is not a parenthesis. if s[index] != '(' and s[index] != ')': recurse(s, index + 1, left_count, right_count, left_rem, right_rem, expr) elif s[index] == '(': # Consider an opening bracket. recurse(s, index + 1, left_count + 1, right_count, left_rem, right_rem, expr) elif s[index] == ')' and left_count > right_count: # Consider a closing bracket. recurse(s, index + 1, left_count, right_count + 1, left_rem, right_rem, expr) # Pop for backtracking. expr.pop() # Now, the left and right variables tell us the number of misplaced left and # right parentheses and that greatly helps pruning the recursion. recurse(s, 0, 0, 0, left, right, []) return list(result.keys())
10. Regular Expression Matching
Given an input string s and a pattern p, implement regular expression matching with support for '.' and '*' where:
'.' Matches any single character. '*' Matches zero or more of the preceding element. The matching should cover the entire input string (not partial).
Example 1: Input: s = "aa", p = "a" Output: false Explanation: "a" does not match the entire string "aa".
Example 2: Input: s = "aa", p = "a" Output: true Explanation: '' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3: Input: s = "ab", p = "." Output: true Explanation: "." means "zero or more (*) of any character (.)".
Constraints:
1 <= s.length <= 20 1 <= p.length <= 30 s contains only lowercase English letters. p contains only lowercase English letters, '.', and ''. It is guaranteed for each appearance of the character '', there will be a previous valid character to match
Algorithm
We proceed with the same recursion as in Approach 1, except because calls will only ever be made to match(text[i:], pattern[j:]), we use \text{dp(i, j)}dp(i, j) to handle those calls instead, saving us expensive string-building operations and allowing us to cache the intermediate results
# DP top down # time = space = O(TP) where T,P = len of text and pattern class Solution(object): def isMatch(self, text, pattern): memo = {} def dp(i, j): if (i, j) not in memo: if j == len(pattern): ans = i == len(text) else: first_match = i < len(text) and pattern[j] in {text[i], '.'} if j+1 < len(pattern) and pattern[j+1] == '*': ans = dp(i, j+2) or first_match and dp(i+1, j) else: ans = first_match and dp(i+1, j+1) memo[i, j] = ans return memo[i, j] return dp(0, 0)
# DP bottom up class Solution(object): def isMatch(self, text, pattern): dp = [[False] * (len(pattern) + 1) for _ in range(len(text) + 1)] dp[-1][-1] = True for i in range(len(text), -1, -1): for j in range(len(pattern) - 1, -1, -1): first_match = i < len(text) and pattern[j] in {text[i], '.'} if j+1 < len(pattern) and pattern[j+1] == '*': dp[i][j] = dp[i][j+2] or first_match and dp[i+1][j] else: dp[i][j] = first_match and dp[i+1][j+1] return dp[0][0]
78. Subsets
Given an integer array nums of unique elements, return all possible subsets (the power set). The solution set must not contain duplicate subsets. Return the solution in any order.
Example 1: Input: nums = [1,2,3] Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
Example 2: Input: nums = [0] Output: [[],[0]]
Constraints:
1 <= nums.length <= 10 -10 <= nums[i] <= 10 All the numbers of nums are unique
Algorithm
We define a backtrack function named backtrack(first, curr) which takes the index of first element to add and a current combination as arguments.
If the current combination is done, we add the combination to the final output.
Otherwise, we iterate over the indexes i from first to the length of the entire sequence n.
Add integer nums[i] into the current combination curr.
Proceed to add more integers into the combination : backtrack(i + 1, curr).
Backtrack by removing nums[i] from curr
# time = O(N*(2^N)), space = O(N) class Solution: def subsets(self, nums: List[int]) -> List[List[int]]: def backtrack(first = 0, curr = []): # if the combination is done if len(curr) == k: output.append(curr[:]) return for i in range(first, n): # add nums[i] into the current combination curr.append(nums[i]) # use next integers to complete the combination backtrack(i + 1, curr) # backtrack curr.pop() output = [] n = len(nums) for k in range(n + 1): backtrack() return output
Part V. Sorting and Searching
29. Divide Two Integers
Given two integers dividend and divisor, divide two integers without using multiplication, division, and mod operator.
The integer division should truncate toward zero, which means losing its fractional part. For example, 8.345 would be truncated to 8, and -2.7335 would be truncated to -2.
Return the quotient after dividing dividend by divisor.
Note: Assume we are dealing with an environment that could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. For this problem, if the quotient is strictly greater than 231 - 1, then return 231 - 1, and if the quotient is strictly less than -231, then return -231.
Example 1:
Input: dividend = 10, divisor = 3 Output: 3 Explanation: 10/3 = 3.33333.. which is truncated to 3.
Example 2: Input: dividend = 7, divisor = -3 Output: -2 Explanation: 7/-3 = -2.33333.. which is truncated to -2.
Constraints:
-231 <= dividend, divisor <= 231 - 1 divisor != 0
Algorithm
Again, we work with negative numbers to elegantly avoid overflow issues.
Hopefully you're getting the hang of the conditionals that have to work with negative, instead of positive, numbers!
# time = space = O(logn) def divide(self, dividend: int, divisor: int) -> int: # Constants. MAX_INT = 2147483647 # 2**31 - 1 MIN_INT = -2147483648 # -2**31 HALF_MIN_INT = -1073741824 # MIN_INT // 2 # Special case: overflow. if dividend == MIN_INT and divisor == -1: return MAX_INT # We need to convert both numbers to negatives. # Also, we count the number of negatives signs. negatives = 2 if dividend > 0: negatives -= 1 dividend = -dividend if divisor > 0: negatives -= 1 divisor = -divisor doubles = [] powersOfTwo = [] # Nothing too exciting here, we're just making a list of doubles of 1 and # the divisor. This is pretty much the same as Approach 2, except we're # actually storing the values this time. */ powerOfTwo = 1 while divisor >= dividend: doubles.append(divisor) powersOfTwo.append(powerOfTwo) # Prevent needless overflows from occurring... if divisor < HALF_MIN_INT: break divisor += divisor # Double divisor powerOfTwo += powerOfTwo # Go from largest double to smallest, checking if the current double fits. # into the remainder of the dividend. quotient = 0 for i in reversed(range(len(doubles))): if doubles[i] >= dividend: # If it does fit, add the current powerOfTwo to the quotient. quotient += powersOfTwo[i] # Update dividend to take into account the bit we've now removed. dividend -= doubles[i] # If there was originally one negative sign, then # the quotient remains negative. Otherwise, switch # it to positive. return quotient if negatives != 1 else -quotient
33. Search in Rotated Sorted Array
There is an integer array nums sorted in ascending order (with distinct values).
Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2].
Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums.
You must write an algorithm with O(log n) runtime complexity.
Example 1: Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4
Example 2: Input: nums = [4,5,6,7,0,1,2], target = 3 Output: -1
Example 3: Input: nums = [1], target = 0 Output: -1
Constraints:
1 <= nums.length <= 5000 -10^4 <= nums[i] <= 10^4 All values of nums are unique. nums is an ascending array that is possibly rotated. -10^4 <= target <= 10^4
Algorithm (binary search in one pass)
As in the normal binary search, we keep two pointers (i.e. start and end) to track the search scope. At each iteration, we reduce the search scope into half, by moving either the start or end pointer to the middle (i.e. mid) of the previous search scope.
Here are the detailed breakdowns of the algorithm:
Initiate the pointer start to 0, and the pointer end to n - 1.
Perform standard binary search. While start <= end:
Take an index in the middle mid as a pivot.
If nums[mid] == target, the job is done, return mid.
Now there could be two situations:
Pivot element is larger than the first element in the array, i.e. the subarray from the first element to the pivot is non-rotated, as shown in the following graph. pic
-
If the target is located in the non-rotated subarray: go left:
end = mid - 1. -
Otherwise: go right:
start = mid + 1. Pivot element is smaller than the first element of the array, i.e. the rotation index is somewhere between 0 and mid. It implies that the sub-array from the pivot element to the last one is non-rotated, as shown in the following graph. pic -
If the target is located in the non-rotated subarray: go right:
start = mid + 1. -
Otherwise: go left:
end = mid - 1.
We're here because the target is not found. Return -1.
# time = O(logn), space = O(1) class Solution: def search(self, nums: List[int], target: int) -> int: start, end = 0, len(nums) - 1 while start <= end: mid = start + (end - start) // 2 if nums[mid] == target: return mid elif nums[mid] >= nums[start]: if target >= nums[start] and target < nums[mid]: end = mid - 1 else: start = mid + 1 else: if target <= nums[end] and target > nums[mid]: start = mid + 1 else: end = mid - 1 return -1
50. Pow(x, n)
Implement pow(x, n), which calculates x raised to the power n (i.e., xn).
Example 1: Input: x = 2.00000, n = 10 Output: 1024.00000
Example 2: Input: x = 2.10000, n = 3 Output: 9.26100
Example 3: Input: x = 2.00000, n = -2 Output: 0.25000 Explanation: 2-2 = 1/22 = 1/4 = 0.25
Constraints:
-100.0 < x < 100.0 -2^31 <= n <= 2^31-1 n is an integer. -10^4 <= xn <= 10^4
Brute force approach: time = O(n), space =O(1), but there is a fas ter way
# time = O(logn), space = O(1) class Solution { public double myPow(double x, int n) { long N = n; if (N < 0) { x = 1 / x; N = -N; } double ans = 1; double current_product = x; for (long i = N; i > 0; i /= 2) { if ((i % 2) == 1) { ans = ans * current_product; } current_product = current_product * current_product; } return ans; } };
162. Find Peak Element
A peak element is an element that is strictly greater than its neighbors.
Given a 0-indexed integer array nums, find a peak element, and return its index. If the array contains multiple peaks, return the index to any of the peaks.
You may imagine that nums[-1] = nums[n] = -∞. In other words, an element is always considered to be strictly greater than a neighbor that is outside the array.
You must write an algorithm that runs in O(log n) time.
Example 1: Input: nums = [1,2,3,1] Output: 2 Explanation: 3 is a peak element and your function should return the index number 2.
Example 2: Input: nums = [1,2,1,3,5,6,4] Output: 5 Explanation: Your function can return either index number 1 where the peak element is 2, or index number 5 where the peak element is 6.
Constraints:
1 <= nums.length <= 1000 -2^31 <= nums[i] <= 2^31 - 1 nums[i] != nums[i + 1] for all valid i.
Algorithm (for recursive (time=space=O(logn)) and iterative (time=O(logn), space=O(1) approaches)
We can view any given sequence in numsnums array as alternating ascending and descending sequences. By making use of this, and the fact that we can return any peak as the result, we can make use of Binary Search to find the required peak element.
In case of simple Binary Search, we work on a sorted sequence of numbers and try to find out the required number by reducing the search space at every step. In this case, we use a modification of this simple Binary Search to our advantage. We start off by finding the middle element, midmid from the given numsnums array. If this element happens to be lying in a descending sequence of numbers. or a local falling slope(found by comparing nums[i]nums[i] to its right neighbour), it means that the peak will always lie towards the left of this element. Thus, we reduce the search space to the left of midmid(including itself) and perform the same process on left subarray.
If the middle element, midmid lies in an ascending sequence of numbers, or a rising slope(found by comparing nums[i]nums[i] to its right neighbour), it obviously implies that the peak lies towards the right of this element. Thus, we reduce the search space to the right of midmid and perform the same process on the right subarray.
In this way, we keep on reducing the search space till we eventually reach a state where only one element is remaining in the search space. This single element is the peak element.
To see how it works, let's consider the three cases discussed above again.
Case 1. In this case, we firstly find 33 as the middle element. Since it lies on a falling slope, we reduce the search space to [1, 2, 3]. For this subarray, 22 happens to be the middle element, which again lies on a falling slope, reducing the search space to [1, 2]. Now, 11 acts as the middle element and it lies on a falling slope, reducing the search space to [1] only. Thus, 11 is returned as the peak correctly.
Case 2. In this case, we firstly find 33 as the middle element. Since it lies on a rising slope, we reduce the search space to [4, 5]. Now, 44 acts as the middle element for this subarray and it lies on a rising slope, reducing the search space to [5] only. Thus, 55 is returned as the peak correctly.
Case 3. In this case, the peak lies somewhere in the middle. The first middle element is 44. It lies on a rising slope, indicating that the peak lies towards its right. Thus, the search space is reduced to [5, 1]. Now, 55 happens to be the on a falling slope(relative to its right neighbour), reducing the search space to [5] only. Thus, 55 is identified as the peak element correctly.
# Java, iterative # time = O(logn), space = O(1) public class Solution { public int findPeakElement(int[] nums) { int l = 0, r = nums.length - 1; while (l < r) { int mid = (l + r) / 2; if (nums[mid] > nums[mid + 1]) r = mid; else l = mid + 1; } return l; } }
278. First Bad Version
You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.
Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.
You are given an API bool isBadVersion(version) which returns whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.
Example 1: Input: n = 5, bad = 4 Output: 4 Explanation: call isBadVersion(3) -> false call isBadVersion(5) -> true call isBadVersion(4) -> true Then 4 is the first bad version.
Example 2: Input: n = 1, bad = 1 Output: 1
Constraints:
1 <= bad <= n <= 231 - 1
# Java, binary search # time = O(logn), space = O(1) public int firstBadVersion(int n) { int left = 1; int right = n; while (left < right) { int mid = left + (right - left) / 2; if (isBadVersion(mid)) { right = mid; } else { left = mid + 1; } } return left; }
349. Intersection of Two Arrays
Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must be unique and you may return the result in any order.
Example 1:
Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2]
Example 2:
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [9,4]
Explanation: [4,9] is also accepted.
Constraints:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
Approach 1: Two Sets
The naive approach would be to iterate along the first array nums1 and to check for each value if this value in nums2 or not. If yes - add the value to output. Such an approach would result in a pretty bad \mathcal{O}(n \times m)O(n×m) time complexity, where n and m are arrays' lengths.
To solve the problem in linear time, let's use the structure set, which provides in/contains operation in O(1) time in average case.
The idea is to convert both arrays into sets, and then iterate over the smallest set checking the presence of each element in the larger set. Time complexity of this approach is O(n+m) in the average case.
Complexity Analysis for Approach 1
Time complexity: O(n+m), where n and m are arrays' lengths. O(n) time is used to convert nums1 into set, O(m) time is used to convert nums2, and contains/in operations are O(1) in the average case.
Space complexity: O(m+n) in the worst case when all elements in the arrays are different.
Approach 2: Built-in Set Intersection
There are built-in intersection facilities, which provide O(n+m) time complexity in the average case and O(n×m) time complexity in the worst case
# Approach 1 # time = O(n+m), space = O(n+m) class Solution: def set_intersection(self, set1, set2): return [x for x in set1 if x in set2] def intersection(self, nums1, nums2): """ :type nums1: List[int] :type nums2: List[int] :rtype: List[int] """ set1 = set(nums1) set2 = set(nums2) if len(set1) < len(set2): return self.set_intersection(set1, set2) else: return self.set_intersection(set2, set1)
# Approach 2 # time = O(nm), space = O(n+m) class Solution: def intersection(self, nums1, nums2): """ :type nums1: List[int] :type nums2: List[int] :rtype: List[int] """ set1 = set(nums1) set2 = set(nums2) return list(set2 & set1)
350. Intersection of Two Arrays II
Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear in any order as many times as it shows in the array where it is less frequent.
Note: on LeetCode, this is phrased: as many time as it shows in both arrays - which is incorrect
Example 1:
Input: nums1 = [1,2,2,1], nums2 = [2,2] Output: [2,2] Example 2:
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4] Output: [4,9] Explanation: [9,4] is also accepted.
My solution below:
* Runtime: 64 ms, faster than 32.78% of Python3 online submissions for Intersection of Two Arrays II.
* Memory Usage: 14.5 MB, less than 41.10% of Python3 online submissions for Intersection of Two Arrays II.
# My solution: time c. O(n^2) def intersect(nums1: List[int], nums2: List[int]) -> List[int]: res = [] common = set(nums1).intersection(set(nums2)) # O(n+m) for i in common: count = min(nums1.count(i), nums2.count(i)) # O(n*(n+m)) res.extend([i]*count) # O(1) or O(n) dep. on if you need to copy array return res # O(n^2)
# LeetCode solution 1: time c. O(n+m) def intersect2(nums1: List[int], nums2: List[int]) -> List[int]: if len(nums1) > len(nums2): return intersect(nums2, nums1) mapp = defaultdict(int) for n in nums1: mapp[n] += 1 k = 0 for n in nums2: if n in mapp and mapp[n] > 0: nums1[k] = n k += 1 mapp[n] -= 1 return nums1[:k+1]
nums1 = [1,2,2,1,4,7] nums2 = [2,2,7] intersect2(nums1, nums2)
[2, 2, 7]
Part VII. Dynamic Programming
32. Longest Valid Parentheses
Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.
Example 1: Input: s = "(()" Output: 2 Explanation: The longest valid parentheses substring is "()".
Example 2: Input: s = ")()())" Output: 4 Explanation: The longest valid parentheses substring is "()()".
Example 3: Input: s = "" Output: 0
Constraints:
0 <= s.length <= 3 * 10^4 s[i] is '(', or ')'
Algorithm (DP)

Algorithm (w/out extra space)
In this approach, we make use of two counters leftleft and rightright. First, we start traversing the string from the left towards the right and for every ‘(’ encountered, we increment the leftleft counter and for every ‘)’ encountered, we increment the rightright counter. Whenever leftleft becomes equal to rightright, we calculate the length of the current valid string and keep track of maximum length substring found so far. If rightright becomes greater than leftleft we reset leftleft and rightright to 00.
# Java, dynamic programming # time = space = O(n) public class Solution { public int longestValidParentheses(String s) { int maxans = 0; int dp[] = new int[s.length()]; for (int i = 1; i < s.length(); i++) { if (s.charAt(i) == ')') { if (s.charAt(i - 1) == '(') { dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; } maxans = Math.max(maxans, dp[i]); } } return maxans; } }
# Java, no extra space # time = O(n), space = O(1) public class Solution { public int longestValidParentheses(String s) { int left = 0, right = 0, maxlength = 0; for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '(') { left++; } else { right++; } if (left == right) { maxlength = Math.max(maxlength, 2 * right); } else if (right >= left) { left = right = 0; } } left = right = 0; for (int i = s.length() - 1; i >= 0; i--) { if (s.charAt(i) == '(') { left++; } else { right++; } if (left == right) { maxlength = Math.max(maxlength, 2 * left); } else if (left >= right) { left = right = 0; } } return maxlength; } }
91. Decode Ways
A message containing letters from A-Z can be encoded into numbers using the following mapping: 'A' -> "1" 'B' -> "2" ... 'Z' -> "26" To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example, "11106" can be mapped into:
"AAJF" with the grouping (1 1 10 6) "KJF" with the grouping (11 10 6) Note that the grouping (1 11 06) is invalid because "06" cannot be mapped into 'F' since "6" is different from "06".
Given a string s containing only digits, return the number of ways to decode it.
The test cases are generated so that the answer fits in a 32-bit integer.
Example 1: Input: s = "12" Output: 2 Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).
Example 2: Input: s = "226" Output: 3 Explanation: "226" could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).
Example 3: Input: s = "06" Output: 0 Explanation: "06" cannot be mapped to "F" because of the leading zero ("6" is different from "06").
Constraints:
1 <= s.length <= 100 s contains only digits and may contain leading zero(s)
Algorithm (recursive w/memoization)
Enter recursion with the given string i.e. start with index 0.
For the terminating case of the recursion we check for the end of the string. If we have reached the end of the string we return 1.
Every time we enter recursion it's for a substring of the original string. For any recursion if the first character is 0 then terminate that path by returning 0. Thus this path won't contribute to the number of ways.
Memoization helps to reduce the complexity which would otherwise be exponential. We check the dictionary memo to see if the result for the given substring already exists.
If the result is already in memo we return the result. Otherwise the number of ways for the given string is determined by making a recursive call to the function with index + 1 for next substring string and index + 2 after checking for valid 2-digit decode. The result is also stored in memo with key as current index, for saving for future overlapping subproblems.
Algorithm (iterative)
If the string s is empty or null we return the result as 0.
Initialize dp array. dp[0] = 1 to provide the baton to be passed.
If the first character of the string is zero then no decode is possible hence initialize dp[1] to 0, otherwise the first character is valid to pass on the baton, dp[1] = 1.
Iterate the dp array starting at index 2. The index i of dp is the i-th character of the string s, that is character at index i-1 of s.
We check if valid single digit decode is possible. This just means the character at index s[i-1] is non-zero. Since we do not have a decoding for zero. If the valid single digit decoding is possible then we add dp[i-1] to dp[i]. Since all the ways up to (i-1)-th character now lead up to i-th character too.
We check if valid two digit decode is possible. This means the substring s[i-2]s[i-1] is between 10 to 26. If the valid two digit decoding is possible then we add dp[i-2] to dp[i].
Once we reach the end of the dp array we would have the number of ways of decoding string s
Algorithm (constant space)
In Approach 2 we are using an array dp to save the results for future. As we move ahead character by character of the given string, we look back only two steps. For calculating dp[i] we need to know dp[i-1] and dp[i-2] only. Thus, we can easily cut down our O(N)O(N) space requirement to O(1)O(1) by using only two variables to store the last two results.
# recirsive w/memoization # time = space = O(n) class Solution: @lru_cache(maxsize=None) def recursiveWithMemo(self, index, s) -> int: # If you reach the end of the string # Return 1 for success. if index == len(s): return 1 # If the string starts with a zero, it can't be decoded if s[index] == '0': return 0 if index == len(s)-1: return 1 answer = self.recursiveWithMemo(index + 1, s) if int(s[index : index + 2]) <= 26: answer += self.recursiveWithMemo(index + 2, s) return answer def numDecodings(self, s: str) -> int: return self.recursiveWithMemo(0, s)
# iterative # time = space = O(n) class Solution: def numDecodings(self, s: str) -> int: # Array to store the subproblem results dp = [0 for _ in range(len(s) + 1)] dp[0] = 1 # Ways to decode a string of size 1 is 1. Unless the string is '0'. # '0' doesn't have a single digit decode. dp[1] = 0 if s[0] == '0' else 1 for i in range(2, len(dp)): # Check if successful single digit decode is possible. if s[i - 1] != '0': dp[i] = dp[i - 1] # Check if successful two digit decode is possible. two_digit = int(s[i - 2 : i]) if two_digit >= 10 and two_digit <= 26: dp[i] += dp[i - 2] return dp[len(s)]
# constant space # time = O(n), space O(1) class Solution: def numDecodings(self, s: str) -> int: if s[0] == "0": return 0 two_back = 1 one_back = 1 for i in range(1, len(s)): current = 0 if s[i] != "0": current = one_back two_digit = int(s[i - 1: i + 1]) if two_digit >= 10 and two_digit <= 26: current += two_back two_back = one_back one_back = current return one_back
121. Best Time to Buy and Sell Stock
You are given an array prices where prices[i] is the price of a given stock on the ith day.
You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.
Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.
Example 1: Input: prices = [7,1,5,3,6,4] Output: 5 Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5. Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.
Example 2: Input: prices = [7,6,4,3,1] Output: 0 Explanation: In this case, no transactions are done and the max profit = 0.
Constraints:
1 <= prices.length <= 10^5 0 <= prices[i] <= 10^4
Algorithm (one pass) The points of interest are the peaks and valleys in the plotted array. We need to find the largest price following each valley, which difference could be the max profit. We can maintain two variables - minprice and maxprofit corresponding to the smallest valley and maximum profit (maximum difference between selling price and minprice) obtained so far respectively.
# one pass # time = O(n), space = O(1) class Solution: def maxProfit(self, prices: List[int]) -> int: min_price = float('inf') max_profit = 0 for i in range(len(prices)): if prices[i] < min_price: min_price = prices[i] elif prices[i] - min_price > max_profit: max_profit = prices[i] - min_price return max_profit
139. Word Break
Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.
Note that the same word in the dictionary may be reused multiple times in the segmentation.
Example 1: Input: s = "leetcode", wordDict = ["leet","code"] Output: true Explanation: Return true because "leetcode" can be segmented as "leet code".
Example 2: Input: s = "applepenapple", wordDict = ["apple","pen"] Output: true Explanation: Return true because "applepenapple" can be segmented as "apple pen apple". Note that you are allowed to reuse a dictionary word.
Example 3: Input: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"] Output: false
Constraints:
1 <= s.length <= 300 1 <= wordDict.length <= 1000 1 <= wordDict[i].length <= 20 s and wordDict[i] consist of only lowercase English letters. All the strings of wordDict are unique.
Algorithm (recursion w/memoization)
In the previous approach we can see that many subproblems were redundant, i.e we were calling the recursive function multiple times for a particular string. To avoid this we can use memoization method, where an array memomemo is used to store the result of the subproblems. Now, when the function is called again for a particular string, value will be fetched and returned using the memomemo array, if its value has been already evaluated.
With memoization many redundant subproblems are avoided and recursion tree is pruned and thus it reduces the time complexity by a large factor.
Algorithm (BFS)
Another approach is to use Breadth-First-Search. Visualize the string as a tree where each node represents the prefix upto index endend. Two nodes are connected only if the substring between the indices linked with those nodes is also a valid string which is present in the dictionary. In order to form such a tree, we start with the first character of the given string (say ss) which acts as the root of the tree being formed and find every possible substring starting with that character which is a part of the dictionary. Further, the ending index (say ii) of every such substring is pushed at the back of a queue which will be used for Breadth First Search. Now, we pop an element out from the front of the queue and perform the same process considering the string s(i+1,end)s(i+1,end) to be the original string and the popped node as the root of the tree this time. This process is continued, for all the nodes appended in the queue during the course of the process. If we are able to obtain the last element of the given string as a node (leaf) of the tree, this implies that the given string can be partitioned into substrings which are all a part of the given dictionary.
Algorithm (DP)

# recursion w/memoization # time = O(n^3), space = O(n) class Solution: def wordBreak(self, s: str, wordDict: List[str]) -> bool: @lru_cache def wordBreakMemo(s: str, word_dict: FrozenSet[str], start: int): if start == len(s): return True for end in range(start + 1, len(s) + 1): if s[start:end] in word_dict and wordBreakMemo(s, word_dict, end): return True return False return wordBreakMemo(s, frozenset(wordDict), 0)
# BFS # time = O(n^3), space = O(n) class Solution: def wordBreak(self, s: str, wordDict: List[str]) -> bool: word_set = set(wordDict) q = deque() visited = set() q.append(0) while q: start = q.popleft() if start in visited: continue for end in range(start + 1, len(s) + 1): if s[start:end] in word_set: q.append(end) if end == len(s): return True visited.add(start) return False
# DP # time = O(n^3), space = O(n) class Solution: def wordBreak(self, s: str, wordDict: List[str]) -> bool: word_set = set(wordDict) dp = [False] * (len(s) + 1) dp[0] = True for i in range(1, len(s) + 1): for j in range(i): if dp[j] and s[j:i] in word_set: dp[i] = True break return dp[len(s)]
304. Range Sum Query 2D - Immutable
Given a 2D matrix matrix, handle multiple queries of the following type:
Calculate the sum of the elements of matrix inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). Implement the NumMatrix class:
NumMatrix(int[][] matrix) Initializes the object with the integer matrix matrix. int sumRegion(int row1, int col1, int row2, int col2) Returns the sum of the elements of matrix inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). You must design an algorithm where sumRegion works on O(1) time complexity.
Example 1:
Input
["NumMatrix", "sumRegion", "sumRegion", "sumRegion"]
[[[[3, 0, 1, 4, 2], [5, 6, 3, 2, 1], [1, 2, 0, 1, 5], [4, 1, 0, 1, 7], [1, 0, 3, 0, 5]]], [2, 1, 4, 3], [1, 1, 2, 2], [1, 2, 2, 4]]
Output
[null, 8, 11, 12]
Explanation NumMatrix numMatrix = new NumMatrix([[3, 0, 1, 4, 2], [5, 6, 3, 2, 1], [1, 2, 0, 1, 5], [4, 1, 0, 1, 7], [1, 0, 3, 0, 5]]); numMatrix.sumRegion(2, 1, 4, 3); // return 8 (i.e sum of the red rectangle) numMatrix.sumRegion(1, 1, 2, 2); // return 11 (i.e sum of the green rectangle) numMatrix.sumRegion(1, 2, 2, 4); // return 12 (i.e sum of the blue rectangle)
Constraints:
m == matrix.length n == matrix[i].length 1 <= m, n <= 200 -10^4 <= matrix[i][j] <= 10^4 0 <= row1 <= row2 < m 0 <= col1 <= col2 < n At most 10^4 calls will be made to sumRegion
# Java # time = O(mn), space = O(mn) class NumMatrix { private int[][] dp; public NumMatrix(int[][] matrix) { if (matrix.length == 0 || matrix[0].length == 0) return; dp = new int[matrix.length + 1][matrix[0].length + 1]; for (int r = 0; r < matrix.length; r++) { for (int c = 0; c < matrix[0].length; c++) { dp[r + 1][c + 1] = dp[r + 1][c] + dp[r][c + 1] + matrix[r][c] - dp[r][c]; } } } public int sumRegion(int row1, int col1, int row2, int col2) { return dp[row2 + 1][col2 + 1] - dp[row1][col2 + 1] - dp[row2 + 1][col1] + dp[row1][col1]; } }
523. Continuous Subarray Sum
Given an integer array nums and an integer k, return true if nums has a continuous subarray of size at least two whose elements sum up to a multiple of k, or false otherwise.
An integer x is a multiple of k if there exists an integer n such that x = n * k. 0 is always a multiple of k.
Example 1: Input: nums = [23,2,4,6,7], k = 6 Output: true Explanation: [2, 4] is a continuous subarray of size 2 whose elements sum up to 6.
Example 2: Input: nums = [23,2,6,4,7], k = 6 Output: true Explanation: [23, 2, 6, 4, 7] is an continuous subarray of size 5 whose elements sum up to 42. 42 is a multiple of 6 because 42 = 7 * 6 and 7 is an integer.
Example 3: Input: nums = [23,2,6,4,7], k = 13 Output: false
Constraints:
1 <= nums.length <= 10^5 0 <= nums[i] <= 10^9 0 <= sum(nums[i]) <= 2^31 - 1 1 <= k <= 2^31 - 1
Algorithm Initialize sum \leftarrow 0, hashMap[0] \leftarrow 0, i <- 0sum←0,hashMap[0]←0,i←0. sum += nums[i]sum+=nums[i]. If hashMaphashMap does not contain key sum \% ksum%k (this remainder modulo kk occurs for the first time) then hashMap[sum % k] <- i + 1hashMap[sum%k]←i+1, go to 5. If hashMap[sum % k] < ihashMap[sum%k]<i (the subarray size is at least two) return true. i += 1i+=1. If i < nums.lengthi<nums.length go to 2. Return false.
# time = O(nums.length), space = O(min{nums.length,k}) class Solution: def checkSubarraySum(self, nums: List[int], k: int) -> bool: # initialize the hash map with index 0 for sum 0 hash_map = {0: 0} s = 0 for i in range(len(nums)): s += nums[i] # if the remainder s % k occurs for the first time if s % k not in hash_map: hash_map[s % k] = i + 1 # if the subarray size is at least two elif hash_map[s % k] < i: return True return False
Part VIII. Design
173. Binary Search Tree Iterator
Implement the BSTIterator class that represents an iterator over the in-order traversal of a binary search tree (BST):
BSTIterator(TreeNode root) Initializes an object of the BSTIterator class. The root of the BST is given as part of the constructor. The pointer should be initialized to a non-existent number smaller than any element in the BST. boolean hasNext() Returns true if there exists a number in the traversal to the right of the pointer, otherwise returns false. int next() Moves the pointer to the right, then returns the number at the pointer. Notice that by initializing the pointer to a non-existent smallest number, the first call to next() will return the smallest element in the BST.
You may assume that next() calls will always be valid. That is, there will be at least a next number in the in-order traversal when next() is called.
Example 1:
Input
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
Output
[null, 3, 7, true, 9, true, 15, true, 20, false]
Explanation BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]); bSTIterator.next(); // return 3 bSTIterator.next(); // return 7 bSTIterator.hasNext(); // return True bSTIterator.next(); // return 9 bSTIterator.hasNext(); // return True bSTIterator.next(); // return 15 bSTIterator.hasNext(); // return True bSTIterator.next(); // return 20 bSTIterator.hasNext(); // return False
Constraints:
The number of nodes in the tree is in the range [1, 105]. 0 <= Node.val <= 106 At most 105 calls will be made to hasNext, and next.
Follow up:
Could you implement next() and hasNext() to run in average O(1) time and use O(h) memory, where h is the height of the tree?
Algorithm (flattening tree)
Initialize an empty array that will contain the nodes of the binary search tree in the sorted order. We traverse the binary search tree in the inorder fashion and for each node that we process, we add it to our array nodes. Note that before processing a node, its left subtree has to be processed (or recursed upon) and after processing a node, its right subtree has to be recursed upon. Once we have all the nodes in an array, we simply need a pointer or an index in that array to implement the two functions next and hasNext. Whenever there's a call to hasNext, we simply check if the index has reached the end of the array or not. For the call to next function, we simply return the element pointed by the index. Also, after a the next function call is made, we have to move the index one step forward to simulate the progress of our iterator.
Algorithm (controlled recursion)
Initialize an empty stack S which will be used to simulate the inorder traversal for our binary search tree. Note that we will be following the same approach for inorder traversal as before except that now we will be using our own stack rather than the system stack. Since we are using a custom data structure, we can pause and resume the recursion at will.
Let's also consider a helper function that we will be calling again and again in the implementation. This function, called _inorder_left will essentially add all the nodes in the leftmost branch of the tree rooted at the given node root to the stack and it will keep on doing so until there is no left child of the root node. Something like the following code:
def inorder_left(root): while (root): S.append(root) root = root.left For a given node root, the next smallest element will always be the leftmost element in its tree. So, for a given root node, we keep on following the leftmost branch until we reach a node which doesn't have a left child and that will be the next smallest element. For the root of our BST, this leftmost node would be the smallest node in the tree. Rest of the nodes are added to the stack because they are pending processing. Try and relate this with a dry run of a simple recursive inorder traversal and things will make a bit more sense.
The first time next() function call is made, the smallest element of the BST has to be returned and then our simulated recursion has to move one step forward i.e. move onto the next smallest element in the BST. The invariant that will be maintained in this algorithm is that the stack top always contains the element to be returned for the next() function call. However, there is additional work that needs to be done to maintain that invariant. It's very easy to implement the hasNext() function since all we need to check is if the stack is empty or not. So, we will only focus on the next() call from now.
Initially, given the root node of the BST, we call the function _inorder_left and that ensures our invariant holds. Let's see this first step with an example.
Suppose we get a call to the next() function. The node which we have to return i.e. the next smallest element in the binary search tree iterator is the one sitting at the top of our stack. So, for the example above, that node would be 2 which is the correct value. Now, there are two possibilities that we have to deal with:
One is where the node at the top of the stack is actually a leaf node. This is the best case and here we don't have to do anything. Simply pop the node off the stack and return its value. So, this would be a constant time operation.
Second is where the node has a right child. We don't need to check for the left child because of the way we have added nodes onto the stack. The topmost node either won't have a left child or would already have the left subtree processed. If it has a right child, then we call our helper function on the node's right child. This would comparatively be a costly operation depending upon the structure of the tree.
We keep on maintaining the invariant this way in the function call for next and this way we will always be able to return the next smallest element in the BST from the top of the stack. Again, it's important to understand that obtaining the next smallest element doesn't take much time. However, some time is spent in maintaining the invariant that the stack top will always have the node we are looking for
# flattening tree # time = space = O(n) # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class BSTIterator: def __init__(self, root: TreeNode): # Array containing all the nodes in the sorted order self.nodes_sorted = [] # Pointer to the next smallest element in the BST self.index = -1 # Call to flatten the input binary search tree self._inorder(root) def _inorder(self, root): if not root: return self._inorder(root.left) self.nodes_sorted.append(root.val) self._inorder(root.right) def next(self) -> int: """ @return the next smallest number """ self.index += 1 return self.nodes_sorted[self.index] def hasNext(self) -> bool: """ @return whether we have a next smallest number """ return self.index + 1 < len(self.nodes_sorted)
# controlled recursion # time = space = O(n) # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class BSTIterator: def __init__(self, root: TreeNode): # Stack for the recursion simulation self.stack = [] # Remember that the algorithm starts with a call to the helper function # with the root node as the input self._leftmost_inorder(root) def _leftmost_inorder(self, root): # For a given node, add all the elements in the leftmost branch of the tree # under it to the stack. while root: self.stack.append(root) root = root.left def next(self) -> int: """ @return the next smallest number """ # Node at the top of the stack is the next smallest element topmost_node = self.stack.pop() # Need to maintain the invariant. If the node has a right child, call the # helper function for the right child if topmost_node.right: self._leftmost_inorder(topmost_node.right) return topmost_node.val def hasNext(self) -> bool: """ @return whether we have a next smallest number """ return len(self.stack) > 0
211. Add and Search Word - Data structure design
This solution uses a standard trie (as opposed to bitwise trie)
Design a data structure that supports adding new words and finding if a string matches any previously added string.
Implement the WordDictionary class:
WordDictionary() Initializes the object. void addWord(word) Adds word to the data structure, it can be matched later. bool search(word) Returns true if there is any string in the data structure that matches word or false otherwise. word may contain dots '.' where dots can be matched with any letter.
Example: Input ["WordDictionary","addWord","addWord","addWord","search","search","search","search"] [[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]] Output [null,null,null,null,false,true,true,true]
Explanation WordDictionary wordDictionary = new WordDictionary(); wordDictionary.addWord("bad"); wordDictionary.addWord("dad"); wordDictionary.addWord("mad"); wordDictionary.search("pad"); // return False wordDictionary.search("bad"); // return True wordDictionary.search(".ad"); // return True wordDictionary.search("b.."); // return True
Constraints:
1 <= word.length <= 25 word in addWord consists of lowercase English letters. word in search consist of '.' or lowercase English letters. There will be at most 3 dots in word for search queries. At most 104 calls will be made to addWord and search.
Algorithm

In trie, each path from the root to the "word" node represents one of the input words, for example, o -> a -> t -> h is "oath".
Trie implementation is pretty straightforward, it's basically nested hashmaps. At each step, one has to verify, if the child node to add is already present. If yes, just go one step down. If not, add it into the trie and then go one step down
# time = space = O(M) class WordDictionary: def __init__(self): """ Initialize your data structure here. """ self.trie = {} def addWord(self, word: str) -> None: """ Adds a word into the data structure. """ node = self.trie for ch in word: if not ch in node: node[ch] = {} node = node[ch] node['$'] = True def search(self, word: str) -> bool: """ Returns if the word is in the data structure. A word could contain the dot character '.' to represent any letter. """ def search_in_node(word, node) -> bool: for i, ch in enumerate(word): if not ch in node: # if the current character is '.' # check all possible nodes at this level if ch == '.': for x in node: if x != '$' and search_in_node(word[i + 1:], node[x]): return True # if no nodes lead to answer # or the current character != '.' return False # if the character is found # go down to the next level in trie else: node = node[ch] return '$' in node return search_in_node(word, self.trie)
421. Maximum XOR of Two Numbers in an Array
This solution uses a bitwise trie (as opposed to standard trie)
Given an integer array nums, return the maximum result of nums[i] XOR nums[j], where 0 <= i <= j < n.
Example 1: Input: nums = [3,10,5,25,2,8] Output: 28 Explanation: The maximum result is 5 XOR 25 = 28.
Example 2: Input: nums = [14,70,53,83,49,91,36,80,92,51,66,70] Output: 127
Constraints:
1 <= nums.length <= 2 * 105 0 <= nums[i] <= 231 - 1
Algorithm
To summarise, now one could
Insert a number into Bitwise Trie.
Find maximum XOR of a given number with all numbers that have been inserted so far.
That's all one needs to solve the initial problem:
Convert all numbers to the binary form.
Add the numbers into Trie one by one and compute the maximum XOR of a number to add with all previously inserted. Update maximum XOR at each step.
Return max_xor.
# time = O(n), swpace = O(1) class Solution: def findMaximumXOR(self, nums: List[int]) -> int: # Compute length L of max number in a binary representation L = len(bin(max(nums))) - 2 # zero left-padding to ensure L bits for each number nums = [[(x >> i) & 1 for i in range(L)][::-1] for x in nums] max_xor = 0 trie = {} for num in nums: node = trie xor_node = trie curr_xor = 0 for bit in num: # insert new number in trie if not bit in node: node[bit] = {} node = node[bit] # to compute max xor of that new number # with all previously inserted toggled_bit = 1 - bit if toggled_bit in xor_node: curr_xor = (curr_xor << 1) | 1 xor_node = xor_node[toggled_bit] else: curr_xor = curr_xor << 1 xor_node = xor_node[bit] max_xor = max(max_xor, curr_xor) return max_xor
Part XIX. Other
282. Expression Add Operators
Given a string num that contains only digits and an integer target, return all possibilities to insert the binary operators '+', '-', and/or '*' between the digits of num so that the resultant expression evaluates to the target value.
Note that operands in the returned expressions should not contain leading zeros.
Example 1: Input: num = "123", target = 6 Output: ["123","1+2+3"] Explanation: Both "123" and "1+2+3" evaluate to 6.
Example 2: Input: num = "232", target = 8 Output: ["23+2","2+32"] Explanation: Both "23+2" and "2+32" evaluate to 8.
Example 3: Input: num = "3456237490", target = 9191 Output: [] Explanation: There are no expressions that can be created from "3456237490" to evaluate to 9191.
Constraints:
1 <= num.length <= 10 num consists of only digits. -2^31 <= target <= 2^31 - 1
Hide Hint #1
Note that a number can contain multiple digits.
Hide Hint #2
Since the question asks us to find all of the valid expressions, we need a way to iterate over all of them. (Hint: Recursion!)
Hide Hint #3
We can keep track of the expression string and evaluate it at the very end. But that would take a lot of time. Can we keep track of the expression's value as well so as to avoid the evaluation at the very end of recursion?
Hide Hint #4
Think carefully about the multiply operator. It has a higher precedence than the addition and subtraction operators.
1 + 2 = 3
1 + 2 - 4 --> 3 - 4 --> -1
1 + 2 - 4 * 12 --> -1 * 12 --> -12 (WRONG!)
1 + 2 - 4 * 12 --> -1 - (-4) + (-4 * 12) --> 3 + (-48) --> -45 (CORRECT!)
Hide Hint #5
We simply need to keep track of the last operand in our expression and reverse it's effect on the expression's value while considering the multiply operator.
Algorithm
Let's quickly look at the steps involved in our backtracking algorithm before looking at the pseudo-code.
As discussed above, we have multiple choices of what operators to use and what the operands can be and hence, we have to look at all the possibilities to find all valid expressions. Our recursive call will have an index which represents the current digit we're looking at in the original nums string and also the expression string built till now. At every step, we have exactly 4 different recursive calls. The NO OP call simply extends the current_operand by the current digit and moves ahead. Rest of the recursive calls correspond to +, -, and *. We keep on building our expression like this and eventually, the entire nums string would be processed. At that time we check if the expression we built till now is a valid expression or not and we record it if it is a valid one. 1. procedure recurse(digits, index, expression): 2. if we have reached the end of the string: 3. if the expression evaluates to the target: 4. Valid Expression found! 5. else: 6. try out operator 'NO OP' and recurse 7. try out operator * and recurse 8. try out operator + and recurse 9. try out operator - and recurse
# time = O(N×(4^N)), space = O(N) where N = nums.length class Solution: def addOperators(self, num: 'str', target: 'int') -> 'List[str]': N = len(num) answers = [] def recurse(index, prev_operand, current_operand, value, string): # Done processing all the digits in num if index == N: # If the final value == target expected AND # no operand is left unprocessed if value == target and current_operand == 0: answers.append("".join(string[1:])) return # Extending the current operand by one digit current_operand = current_operand*10 + int(num[index]) str_op = str(current_operand) # To avoid cases where we have 1 + 05 or 1 * 05 since 05 won't be a # valid operand. Hence this check if current_operand > 0: # NO OP recursion recurse(index + 1, prev_operand, current_operand, value, string) # ADDITION string.append('+'); string.append(str_op) recurse(index + 1, current_operand, 0, value + current_operand, string) string.pop();string.pop() # Can subtract or multiply only if there are some previous operands if string: # SUBTRACTION string.append('-'); string.append(str_op) recurse(index + 1, -current_operand, 0, value - current_operand, string) string.pop();string.pop() # MULTIPLICATION string.append('*'); string.append(str_op) recurse(index + 1, current_operand * prev_operand, 0, value - prev_operand + (current_operand * prev_operand), string) string.pop();string.pop() recurse(0, 0, 0, 0, []) return answers
438. Find All Anagrams in a String
Given two strings s and p, return an array of all the start indices of p's anagrams in s. You may return the answer in any order.
An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
Example 1: Input: s = "cbaebabacd", p = "abc" Output: [0,6] Explanation: The substring with start index = 0 is "cba", which is an anagram of "abc". The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2: Input: s = "abab", p = "ab" Output: [0,1,2] Explanation: The substring with start index = 0 is "ab", which is an anagram of "ab". The substring with start index = 1 is "ba", which is an anagram of "ab". The substring with start index = 2 is "ab", which is an anagram of "ab".
Constraints:
1 <= s.length, p.length <= 3 * 10^4 s and p consist of lowercase English letters
__Algorithm (sliding window w/hashmap)
Build reference counter pCount for string p.
Move sliding window along the string s:
Recompute sliding window counter sCount at each step by adding one letter on the right and removing one letter on the left.
If sCount == pCount, update the output list.
Return output list.
Algorithm (sliding window w/array)
Hashmap is quite complex structure, with known performance issues in Java. Let's implement approach 1 using 26-elements array instead of hashmap
Build reference array pCount for string p.
Move sliding window along the string s:
Recompute sliding window array sCount at each step by adding one letter on the right and removing one letter on the left.
If sCount == pCount, update the output list.
Return output list
# sliding window w/hashmap # time = O(s.length), space = O(K) where K = max possible # distinct chars (26) from collections import Counter class Solution: def findAnagrams(self, s: str, p: str) -> List[int]: ns, np = len(s), len(p) if ns < np: return [] p_count = Counter(p) s_count = Counter() output = [] # sliding window on the string s for i in range(ns): # add one more letter # on the right side of the window s_count[s[i]] += 1 # remove one letter # from the left side of the window if i >= np: if s_count[s[i - np]] == 1: del s_count[s[i - np]] else: s_count[s[i - np]] -= 1 # compare array in the sliding window # with the reference array if p_count == s_count: output.append(i - np + 1) return output
# sliding window w/array # time = O(s.length), space = O(K) where K = max possible # distinct chars (26) class Solution: def findAnagrams(self, s: str, p: str) -> List[int]: ns, np = len(s), len(p) if ns < np: return [] p_count, s_count = [0] * 26, [0] * 26 # build reference array using string p for ch in p: p_count[ord(ch) - ord('a')] += 1 output = [] # sliding window on the string s for i in range(ns): # add one more letter # on the right side of the window s_count[ord(s[i]) - ord('a')] += 1 # remove one letter # from the left side of the window if i >= np: s_count[ord(s[i - np]) - ord('a')] -= 1 # compare array in the sliding window # with the reference array if p_count == s_count: output.append(i - np + 1) return output
567. Permutation in String (is it same as 438)
Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise.
In other words, return true if one of s1's permutations is the substring of s2.
Example 1: Input: s1 = "ab", s2 = "eidbaooo" Output: true Explanation: s2 contains one permutation of s1 ("ba").
Example 2: Input: s1 = "ab", s2 = "eidboaoo" Output: false
Constraints:
1 <= s1.length, s2.length <= 10^4 s1 and s2 consist of lowercase English letters
# Java # time = O(l_1+(l_2-l_1)), space = O(1) where l_1 = len string s_1 and l_2 = len string s_2 public class Solution { public boolean checkInclusion(String s1, String s2) { if (s1.length() > s2.length()) return false; int[] s1map = new int[26]; int[] s2map = new int[26]; for (int i = 0; i < s1.length(); i++) { s1map[s1.charAt(i) - 'a']++; s2map[s2.charAt(i) - 'a']++; } int count = 0; for (int i = 0; i < 26; i++) { if (s1map[i] == s2map[i]) count++; } for (int i = 0; i < s2.length() - s1.length(); i++) { int r = s2.charAt(i + s1.length()) - 'a', l = s2.charAt(i) - 'a'; if (count == 26) return true; s2map[r]++; if (s2map[r] == s1map[r]) { count++; } else if (s2map[r] == s1map[r] + 1) { count--; } s2map[l]--; if (s2map[l] == s1map[l]) { count++; } else if (s2map[l] == s1map[l] - 1) { count--; } } return count == 26; } }
953. Verifying an Alien Dictionary
In an alien language, surprisingly, they also use English lowercase letters, but possibly in a different order. The order of the alphabet is some permutation of lowercase letters.
Given a sequence of words written in the alien language, and the order of the alphabet, return true if and only if the given words are sorted lexicographically in this alien language.
Example 1: Input: words = ["hello","leetcode"], order = "hlabcdefgijkmnopqrstuvwxyz" Output: true Explanation: As 'h' comes before 'l' in this language, then the sequence is sorted.
Example 2: Input: words = ["word","world","row"], order = "worldabcefghijkmnpqstuvxyz" Output: false Explanation: As 'd' comes after 'l' in this language, then words[0] > words[1], hence the sequence is unsorted.
Example 3: Input: words = ["apple","app"], order = "abcdefghijklmnopqrstuvwxyz" Output: false Explanation: The first three characters "app" match, and the second string is shorter (in size.) According to lexicographical rules "apple" > "app", because 'l' > '∅', where '∅' is defined as the blank character which is less than any other character (More info).
Constraints:
1 <= words.length <= 100 1 <= words[i].length <= 20 order.length == 26 All characters in words[i] and order are English lowercase letters
Algorithm
Initialize a hashmap/array to record the relations between each letter and its ranking in order. Iterate over words and compare each pair of adjacent words. Iterate over each letter to find the first different letter between words[i] and words[i + 1]. If words[i + 1] ends before words[i] and no different letters are found, then we need to return false because words[i + 1] should come before words[i] (for example, apple and app). If we find the first different letter and the two words are in the correct order, then we can exit from the current iteration and proceed to the next pair of words. If we find the first different letter and the two words are in the wrong order, then we can safely return false. If we reach this point, it means that we have examined all pairs of adjacent words and that they are all sorted. Therefore we can return true
# time = O(M), space = O(1) where M = total # chars in words class Solution: def isAlienSorted(self, words: List[str], order: str) -> bool: order_map = {} for index, val in enumerate(order): order_map[val] = index for i in range(len(words) - 1): for j in range(len(words[i])): # If we do not find a mismatch letter between words[i] and words[i + 1], # we need to examine the case when words are like ("apple", "app"). if j >= len(words[i + 1]): return False if words[i][j] != words[i + 1][j]: if order_map[words[i][j]] > order_map[words[i + 1][j]]: return False # if we find the first different character and they are sorted, # then there's no need to check remaining letters break return True
986. Interval List Intersections
You are given two lists of closed intervals, firstList and secondList, where firstList[i] = [starti, endi] and secondList[j] = [startj, endj]. Each list of intervals is pairwise disjoint and in sorted order.
Return the intersection of these two interval lists.
A closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b.
The intersection of two closed intervals is a set of real numbers that are either empty or represented as a closed interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3].
Example 1:
Input: firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]] Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
Example 2: Input: firstList = [[1,3],[5,9]], secondList = [] Output: []
Constraints:
0 <= firstList.length, secondList.length <= 1000 firstList.length + secondList.length >= 1 0 <= start_i < end_i <= 10^9 end_i < start_i+1 0 <= start_j < end_j <= 10^9 end_j < startj+1
Algorithm
If A[0] has the smallest endpoint, it can only intersect B[0]. After, we can discard A[0] since it cannot intersect anything else.
Similarly, if B[0] has the smallest endpoint, it can only intersect A[0], and we can discard B[0] after since it cannot intersect anything else.
We use two pointers, i and j, to virtually manage "discarding" A[0] or B[0] repeatedly.
# time = O(M+N), where M, N are the lengths of A and B, respectively class Solution: def intervalIntersection(self, A: List[List[int]], B: List[List[int]]) -> List[List[int]]: ans = [] i = j = 0 while i < len(A) and j < len(B): # Let's check if A[i] intersects B[j]. # lo - the startpoint of the intersection # hi - the endpoint of the intersection lo = max(A[i][0], B[j][0]) hi = min(A[i][1], B[j][1]) if lo <= hi: ans.append([lo, hi]) # Remove the interval with the smallest endpoint if A[i][1] < B[j][1]: i += 1 else: j += 1 return ans
Part X. SQL
176. Second Highest Salary
Solution Table: Employee
+-------------+------+
| Column Name | Type |
+-------------+------+
| id | int |
| salary | int |
+-------------+------+
id is the primary key column for this table. Each row of this table contains information about the salary of an employee.
Write an SQL query to report the second highest salary from the Employee table. If there is no second highest salary, the query should report null.
The query result format is in the following example.
Example 1:
Input: Employee table:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
Output:
+---------------------+
| SecondHighestSalary |
+---------------------+
| 200 |
+---------------------+
Example 2:
Input: Employee table:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
+----+--------+
Output:
+---------------------+
| SecondHighestSalary |
+---------------------+
| null |
+---------------------+
SELECT DISTINCT Salary AS SecondHighestSalary FROM Employee ORDER BY Salary DESC LIMIT 1 OFFSET 1
177. Nth Highest Salary
Table: Employee
+-------------+------+
| Column Name | Type |
+-------------+------+
| id | int |
| salary | int |
+-------------+------+
id is the primary key column for this table. Each row of this table contains information about the salary of an employee.
Write an SQL query to report the nth highest salary from the Employee table. If there is no nth highest salary, the query should report null.
The query result format is in the following example.
Example 1:
Input: Employee table:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
n = 2 Output:
+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200 |
+------------------------+
Example 2:
Input: Employee table:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
+----+--------+
n = 2 Output:
+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| null |
+------------------------+
No official solution on Leetcode (see Leetcode discussions)
183. Customers Who Never Order
Solution Table: Customers
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| id | int |
| name | varchar |
+-------------+---------+
id is the primary key column for this table. Each row of this table indicates the ID and name of a customer.
Table: Orders
+-------------+------+
| Column Name | Type |
+-------------+------+
| id | int |
| customerId | int |
+-------------+------+
id is the primary key column for this table. customerId is a foreign key of the ID from the Customers table. Each row of this table indicates the ID of an order and the ID of the customer who ordered it.
Write an SQL query to report all customers who never order anything.
Return the result table in any order.
The query result format is in the following example.
Example 1:
Input: Customers table:
+----+-------+
| id | name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+
Orders table:
+----+------------+
| id | customerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+
Output:
+-----------+
| Customers |
+-----------+
| Henry |
| Max |
+-----------+
select customers.name as 'Customers' from customers where customers.id not in ( select customerid from orders );
597. Friend Requests I: Overall Acceptance Rate
Solution Table: FriendRequest
+----------------+---------+
| Column Name | Type |
+----------------+---------+
| sender_id | int |
| send_to_id | int |
| request_date | date |
+----------------+---------+
There is no primary key for this table, it may contain duplicates. This table contains the ID of the user who sent the request, the ID of the user who received the request, and the date of the request.
Table: RequestAccepted
+----------------+---------+
| Column Name | Type |
+----------------+---------+
| requester_id | int |
| accepter_id | int |
| accept_date | date |
+----------------+---------+
There is no primary key for this table, it may contain duplicates. This table contains the ID of the user who sent the request, the ID of the user who received the request, and the date when the request was accepted.
Write an SQL query to find the overall acceptance rate of requests, which is the number of acceptance divided by the number of requests. Return the answer rounded to 2 decimals places.
Note that:
The accepted requests are not necessarily from the table friend_request. In this case, Count the total accepted requests (no matter whether they are in the original requests), and divide it by the number of requests to get the acceptance rate. It is possible that a sender sends multiple requests to the same receiver, and a request could be accepted more than once. In this case, the ‘duplicated’ requests or acceptances are only counted once. If there are no requests at all, you should return 0.00 as the accept_rate. The query result format is in the following example.
Example 1:
Input: FriendRequest table:
+-----------+------------+--------------+
| sender_id | send_to_id | request_date |
+-----------+------------+--------------+
| 1 | 2 | 2016/06/01 |
| 1 | 3 | 2016/06/01 |
| 1 | 4 | 2016/06/01 |
| 2 | 3 | 2016/06/02 |
| 3 | 4 | 2016/06/09 |
+-----------+------------+--------------+
RequestAccepted table:
+--------------+-------------+-------------+
| requester_id | accepter_id | accept_date |
+--------------+-------------+-------------+
| 1 | 2 | 2016/06/03 |
| 1 | 3 | 2016/06/08 |
| 2 | 3 | 2016/06/08 |
| 3 | 4 | 2016/06/09 |
| 3 | 4 | 2016/06/10 |
+--------------+-------------+-------------+
Output:
+-------------+
| accept_rate |
+-------------+
| 0.8 |
+-------------+
Explanation: There are 4 unique accepted requests, and there are 5 requests in total. So the rate is 0.80.
Follow up:
Could you write a query to return the acceptance rate for every month? Could you write a query to return the cumulative acceptance rate for every day?
Hint #1
Still remember how to count the number of rows in a table?
Hint #2
What is the keyword to filter the duplicated records in a table?
SELECT ROUND( IFNULL( (SELECT COUNT(*) FROM (SELECT DISTINCT requester_id, accepter_id FROM RequestAccepted) AS A) / (SELECT COUNT(*) FROM (SELECT DISTINCT sender_id, send_to_id FROM FriendRequest) AS B), 0) , 2) AS accept_rate;
602. Friend Requests II: Who Has the Most Friends
Table: RequestAccepted
+----------------+---------+
| Column Name | Type |
+----------------+---------+
| requester_id | int |
| accepter_id | int |
| accept_date | date |
+----------------+---------+
(requester_id, accepter_id) is the primary key for this table. This table contains the ID of the user who sent the request, the ID of the user who received the request, and the date when the request was accepted.
Write an SQL query to find the people who have the most friends and the most friends number.
The test cases are generated so that only one person has the most friends.
The query result format is in the following example.
Example 1:
Input: RequestAccepted table:
+--------------+-------------+-------------+
| requester_id | accepter_id | accept_date |
+--------------+-------------+-------------+
| 1 | 2 | 2016/06/03 |
| 1 | 3 | 2016/06/08 |
| 2 | 3 | 2016/06/08 |
| 3 | 4 | 2016/06/09 |
+--------------+-------------+-------------+
Output:
+----+-----+
| id | num |
+----+-----+
| 3 | 3 |
+----+-----+
Explanation: The person with id 3 is a friend of people 1, 2, and 4, so he has three friends in total, which is the most number than any others.
Follow up: In the real world, multiple people could have the same most number of friends. Could you find all these people in this case?
Hint #1
Being friends is bidirectional. If you accept someone's adding friend request, both you and the other person will have one more friend.
No official solution on Leetcode (see Leetcode discussions for this problem)
185. Department Top Three Salaries
Table: Employee
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| id | int |
| name | varchar |
| salary | int |
| departmentId | int |
+--------------+---------+
id is the primary key column for this table. departmentId is a foreign key of the ID from the Department table. Each row of this table indicates the ID, name, and salary of an employee. It also contains the ID of their department.
Table: Department
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| id | int |
| name | varchar |
+-------------+---------+
id is the primary key column for this table. Each row of this table indicates the ID of a department and its name.
A company's executives are interested in seeing who earns the most money in each of the company's departments. A high earner in a department is an employee who has a salary in the top three unique salaries for that department.
Write an SQL query to find the employees who are high earners in each of the departments.
Return the result table in any order.
The query result format is in the following example.
Example 1:
Input: Employee table:
+----+-------+--------+--------------+
| id | name | salary | departmentId |
+----+-------+--------+--------------+
| 1 | Joe | 85000 | 1 |
| 2 | Henry | 80000 | 2 |
| 3 | Sam | 60000 | 2 |
| 4 | Max | 90000 | 1 |
| 5 | Janet | 69000 | 1 |
| 6 | Randy | 85000 | 1 |
| 7 | Will | 70000 | 1 |
+----+-------+--------+--------------+
Department table:
+----+-------+
| id | name |
+----+-------+
| 1 | IT |
| 2 | Sales |
+----+-------+
Output:
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT | Max | 90000 |
| IT | Joe | 85000 |
| IT | Randy | 85000 |
| IT | Will | 70000 |
| Sales | Henry | 80000 |
| Sales | Sam | 60000 |
+------------+----------+--------+
Explanation: In the IT department: - Max earns the highest unique salary - Both Randy and Joe earn the second-highest unique salary - Will earns the third-highest unique salary
In the Sales department: - Henry earns the highest salary - Sam earns the second-highest salary - There is no third-highest salary as there are only two employees
SELECT d.Name AS 'Department', e1.Name AS 'Employee', e1.Salary FROM Employee e1 JOIN Department d ON e1.DepartmentId = d.Id WHERE 3 > (SELECT COUNT(DISTINCT e2.Salary) FROM Employee e2 WHERE e2.Salary > e1.Salary AND e1.DepartmentId = e2.DepartmentId ) ;
Check the Google notebook for the following problems that are also in Facebook Section
- Longest Palindromic Substring
- Letter Combinations of a Phone Number
- Find First and Last Position of Element in Sorted Array
- Merge Intervals
- LRU Cache
- Strobogrammatic Number II
- Serialize and Deserialize Binary Tree
Check if a given string is palindrome E.g. "ab!,b,a" -> abba => true "A man,n!! am a" -> amannama true "ab!,ab," -> abab => false "A man, a plan, a canal, Panama!" => true "ab!,b,,A" -> abba def isPalindrome(s: str) -> bool: if not isinstance(s, str): raise ValueError('Please input a string') if len(s) < 2: return True l, r = 0, len(s)-1 while l < r: while not s[l].islanum(): l += 1 while not s[r].islanum(): r -= 1 if s[l].lower() != s[r].lower(): return False l += 1 r -= 1 return True =========================== Iterate list backwards. Print on every node E.g Input list 4->3->2->1->null Output 1,2,3,4 Input : head of the list, 4 class Node { Node next; Node prev; int val; }; def printBackwords(node: Node) -> None: if not isinstance(node, Node): raise ValueError('m') if not node: print() arr = [] while node.next: arr.append(node.val) node = node.next print(arr[::-1]) 4->3->2->1->null def printBackwords(node: Node) -> None: if not isinstance(node, Node): raise ValueError('m') if not node: print() prev = Node(None) while node.next: temp = node.next node.next = prev prev = node node = temp
Source notebook: algo09_fb_recruitment_portal.ipynb
Facebook Recruiting Portal Coding Questions
https://www.facebookrecruiting.com/portal/coding_practice_question/
1. Arrays
a. Reverse to Make Equal
Given two arrays A and B of length N, determine if there is a way to make A equal to B by reversing any subarrays from array B any number of times.
Idea - reversing 2-element subarray (pairs) many times = sorting
Signature
bool areTheyEqual(int[] arr_a, int[] arr_b)
Input
All integers in array are in the range [0, 1,000,000,000].
Output
Return true if B can be made equal to A, return false otherwise.
Example
A = [1, 2, 3, 4]
B = [1, 4, 3, 2]
output = true
After reversing the subarray of B from indices 1 to 3, array B will equal array A.
import math # Add any extra import statements you may need here # Add any helper functions you may need here def are_they_equal(a, b): ''' If it's reversing any subarray any number of times - this is very flexible, and means just sorting. Just checking if elements are the same using a set: O(n) time! ''' if len(a) != len(b): return False elements = set() for element in a: elements.add( element ) first_len = len(elements) for element in b: elements.add( element ) return len( elements ) == first_len def are_they_equal2(a, b): ''' If it's reversing any subarray any number of times - this is very flexible, and means just sorting. Using a sorting algo: O(nlogn) time ''' if len(a) != len(b): return False a.sort() b.sort() for i in range(len(a)): if (a[i] != b[i]): return False return True # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printString(string): print('[\"', string, '\"]', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printString(expected) print(' Your output: ', end='') printString(output) print() test_case_number += 1 if __name__ == "__main__": n_1 = 4 a_1 = [1, 2, 3, 4] b_1 = [1, 4, 3, 2] expected_1 = True output_1 = are_they_equal(a_1, b_1) check(expected_1, output_1) n_2 = 4 a_2 = [1, 2, 3, 4] b_2 = [1, 2, 3, 5] expected_2 = False output_2 = are_they_equal(a_2, b_2) check(expected_2, output_2) n_3 = 4 a_3 = [1, 2, 3, 4] b_3 = [1, 2, 3] expected_3 = False output_3 = are_they_equal(a_3, b_3) check(expected_3, output_3) # Add your own test cases here
✓Test #1 ✓Test #2 ✓Test #3
b. Contiguous Subarrays
Array arr of N integers. For each index i find # contiguous subarrays such that: * value at i == max in contiguous subarrays, * contiguous subarrays must either start or end on i
Input
Array arr is a non-empty list of unique integers that range between 1 to 1,000,000,000
Size N is between 1 and 1,000,000
Output
An array where each index i contains an integer denoting the maximum number of contiguous subarrays of arr[i]
Example
arr = [3, 4, 1, 6, 2]
output = [1, 3, 1, 5, 1]
Explanation:
For index 0 - [3] is the only contiguous subarray that starts (or ends) with 3, and the maximum value in this subarray is 3.
For index 1 - [4], [3, 4], [4, 1]
For index 2 - [1]
For index 3 - [6], [6, 2], [1, 6], [4, 1, 6], [3, 4, 1, 6]
For index 4 - [2]
So, the answer for the above input is [1, 3, 1, 5, 1]
def count_subarrays(arr): ''' O(nlogn) - because we don't check all n options at n positions (n = len(arr)), but stop when value at i is not max ''' res = [] for i, it in enumerate(arr): count = 1 current = i while current > 0: current -= 1 if it > arr[current]: count += 1 else: break current = i while current < len(arr)-1: current += 1 if it > arr[current]: count += 1 else: break res.append(count) return res def count_subarrays2(arr): ''' O(n^2) - checking n options at n positions (n = len(arr)) ''' res = [] for i, it in enumerate(arr): count = 1 if i != 0: j = i while j > 0: j -= 1 sub = arr[ j : i+1 ] if it == max(sub): count += 1 if i != len(arr)-1: j = i while j < len(arr)-1: j += 1 sub = arr[ i : j+1 ] if it == max(sub): count += 1 res.append(count) return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": test_1 = [3, 4, 1, 6, 2] expected_1 = [1, 3, 1, 5, 1] output_1 = count_subarrays(test_1) check(expected_1, output_1) test_2 = [2, 4, 7, 1, 5, 3] expected_2 = [1, 2, 6, 1, 3, 1] output_2 = count_subarrays(test_2) check(expected_2, output_2) test_3 = [20, 40, 70] expected_3 = [1, 2, 3] output_3 = count_subarrays(test_3) check(expected_3, output_3)
✓Test #1 ✓Test #2 ✓Test #3
c. Passing Yearbooks
There are n students, numbered from 1 to n, each with their own yearbook. They would like to pass their yearbooks around and get them signed by other students.
You're given a list of n integers arr[1..n], which is guaranteed to be a permutation of 1..n (in other words, it includes the integers from 1 to n exactly once each, in some order). The meaning of this list is described below. Initially, each student is holding their own yearbook. The students will then repeat the following two steps each minute: Each student i will first sign the yearbook that they're currently holding (which may either belong to themselves or to another student), and then they'll pass it to student arr[i-1]. It's possible that arr[i-1] = i for any given i, in which case student i will pass their yearbook back to themselves. Once a student has received their own yearbook back, they will hold on to it and no longer participate in the passing process.
It's guaranteed that, for any possible valid input, each student will eventually receive their own yearbook back and will never end up holding more than one yearbook at a time.
You must compute a list of n integers output, whose element at i-1 is equal to the number of signatures that will be present in student i's yearbook once they receive it back.
Input
n is in the range [1, 100,000].
Each value arr[i] is in the range [1, n], and all values in arr[i] are distinct.
Output
Return a list of n integers output, as described above.
Example 1
n = 2
arr = [2, 1]
output = [2, 2]
Pass 1:
Student 1 signs their own yearbook. Then they pass the book to the student at arr[0], which is Student 2.
Student 2 signs their own yearbook. Then they pass the book to the student at arr[1], which is Student 1.
Pass 2:
Student 1 signs Student 2's yearbook. Then they pass it to the student at arr[0], which is Student 2.
Student 2 signs Student 1's yearbook. Then they pass it to the student at arr[1], which is Student 1.
Pass 3:
Both students now hold their own yearbook, so the process is complete.
Each student received 2 signatures.
Example 2
n = 2
arr = [1, 2]
output = [1, 1]
Pass 1:
Student 1 signs their own yearbook. Then they pass the book to the student at arr[0], which is themself, Student 1.
Student 2 signs their own yearbook. Then they pass the book to the student at arr[1], which is themself, Student 2.
Pass 2:
Both students now hold their own yearbook, so the process is complete.
Each student received 1 signature.
SOLUTION
https://leetcode.com/discuss/interview-question/614096/facebook-interview-preparation-question-passing-yearbooks
Had to really understand this conceptually to realize an O(N) time/space (Python) solution.
Consider that each student is a member of a single yearbook passing cycle. For example, for input [3, 2, 4, 1], there are two yearbook passing cycles: {3, 4, 1} and {2}. Therefore, the number of signatures for each member of a passing cycle is equal to the size of the cycle. In the example, students {3, 4, 1} each get 3 signatures (1 for his/her self, and 1 for the other 2 students), and student {2} gets 1 signature, his/her own signature. The yearbook passing cycle for any group of students can be determined starting with a particular student, and traversing through the input until the next student to receive the book is the first student of the cycle. (I thought of the cycle like a circular linked list.)
An O(N) time algo can achieved if it considers a student only one time. That is, as the root of a passing cycle, or as a member of a passing cycle that is visited before the traversal returns to the root member.
Algo
Follow the passing cycle of the first student until it returns back to the first student. Along the way, mark each student (the root and traversed nodes) as visited. Save the count of the members in the cycle with the root node. For each node traversed (nodes other than the root node), save a reference to the root node. Repeat the above for any students that have yet to be visited. For each root node, return the size of its cycle. For each traversed node, return the size of the cycle of its root node.
def findSignatureCounts(arr): visited_students = set() signatures = [0] * len(arr) root_indexes = [-1] * len(arr) for i in range(len(arr)): # avoid traversing a node more than once student = arr[i] if student in visited_students: continue visited_students.add(student) # consider the current student to be the root of a cycle, and traverse back to itself signatures[i] = 1 next_i = student - 1 while next_i != i: signatures[i] += 1 root_indexes[next_i] = i visited_students.add(arr[next_i]) next_i = arr[next_i] - 1 # return the signature counts of the root nodes, and the referenced root node counts of traversed nodes for i in range(len(arr)): if root_indexes[i] != -1: signatures[i] = signatures[root_indexes[i]] return signatures # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": arr_1 = [2, 1] expected_1 = [2, 2] output_1 = findSignatureCounts(arr_1) check(expected_1, output_1) arr_2 = [1, 2] expected_2 = [1, 1] output_2 = findSignatureCounts(arr_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 [2, 2] Your output: [2, 2] ✓Test #2 [1, 1] Your output: [1, 1]
2. Starter Plan
a. Rotational Cipher
One simple way to encrypt a string is to "rotate" every alphanumeric character by a certain amount. Rotating a character means replacing it with another character that is a certain number of steps away in normal alphabetic or numerical order.
For example, if the string "Zebra-493?" is rotated 3 places, the resulting string is "Cheud-726?". Every alphabetic character is replaced with the character 3 letters higher (wrapping around from Z to A), and every numeric character replaced with the character 3 digits higher (wrapping around from 9 to 0). Note that the non-alphanumeric characters remain unchanged.
Given a string and a rotation factor, return an encrypted string.
Input
1 <= |input| <= 1,000,000
0 <= rotationFactor <= 1,000,000
Output
Return the result of rotating input a number of times equal to rotationFactor.
Example 1
input = Zebra-493?
rotationFactor = 3
output = Cheud-726?
Example 2
input = abcdefghijklmNOPQRSTUVWXYZ0123456789
rotationFactor = 39
output = nopqrstuvwxyzABCDEFGHIJKLM9012345678
def rotationalCipher(text, shift): result = '' for char in text: # encrypt digit if char.isdigit(): result += str( int((int(char) + shift) % 10) ) # encrypt uppercase elif char.isupper(): result += chr((ord(char) + shift - 65) % 26 + 65) # encrypt lowercase elif char.islower(): result += chr((ord(char) + shift - 97) % 26 + 97) else: result += char return result # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printString(string): print('[\"', string, '\"]', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printString(expected) print(' Your output: ', end='') printString(output) print() test_case_number += 1 if __name__ == "__main__": input_1 = "All-convoYs-9-be:Alert1." rotation_factor_1 = 4 expected_1 = "Epp-gsrzsCw-3-fi:Epivx5." output_1 = rotationalCipher(input_1, rotation_factor_1) check(expected_1, output_1) input_2 = "abcdZXYzxy-999.@" rotation_factor_2 = 200 expected_2 = "stuvRPQrpq-999.@" output_2 = rotationalCipher(input_2, rotation_factor_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
b. Contiguous Subarrays (see above)
c. Pair Sums
Given a list of n integers arr[0..(n-1)], determine the number of different pairs of elements within it which sum to k.
If an integer appears in the list multiple times, each copy is considered to be different; that is, two pairs are considered different if one pair includes at least one array index which the other doesn't, even if they include the same values.
Input
n is in the range [1, 100,000].
Each value arr[i] is in the range [1, 1,000,000,000].
k is in the range [1, 1,000,000,000].
Output
Return the number of different pairs of elements which sum to k.
Example 1 n = 5 k = 6 arr = [1, 2, 3, 4, 3] output = 2 The valid pairs are 2+4 and 3+3.
Example 2
n = 5
k = 6
arr = [1, 5, 3, 3, 3]
output = 4
There's one valid pair 1+5, and three different valid pairs 3+3 (the 3rd and 4th elements, 3rd and 5th elements, and 4th and 5th elements).
def numberOfWays(arr, k): # first create a map d, count = dict(), 0 for i, e in enumerate(arr): if e in d: d[e] += 1 else: d[e] = 1 # iterate & compare, every pair is counted twice! for e in arr: if k-e in d: count += d[k-e] # to avoid paring e with itself at the same index if k/2 == e if k-e == e: count -= 1 return count/2 # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": k_1 = 6 arr_1 = [1, 2, 3, 4, 3] expected_1 = 2 output_1 = numberOfWays(arr_1, k_1) check(expected_1, output_1) k_2 = 6 arr_2 = [1, 5, 3, 3, 3] expected_2 = 4 output_2 = numberOfWays(arr_2, k_2) check(expected_2, output_2)
✓Test #1 ✓Test #2
3. Strings
a. Rotational Cypher (see above)
c. Matching Pairs
Given two strings s and t of length N, find the maximum number of possible matching pairs in strings s and t after swapping exactly two characters within s.
A swap is switching s[i] and s[j], where s[i] and s[j] denotes the character that is present at the ith and jth index of s, respectively. The matching pairs of the two strings are defined as the number of indices for which s[i] and t[i] are equal.
Note: This means you must swap two characters at different indices.
Input
s and t are strings of length N
N is between 2 and 1,000,000
Output
Return an integer denoting the maximum number of matching pairs
Example 1
s = "abcd"
t = "adcb"
output = 4
Explanation:
Using 0-based indexing, and with i = 1 and j = 3, s[1] and s[3] can be swapped, making it "adcb".
Therefore, the number of matching pairs of s and t will be 4.
Example 2
s = "mno"
t = "mno"
output = 1
Explanation:
Two indices have to be swapped, regardless of which two it is, only one letter will remain the same. If i = 0 and j=1, s[0] and s[1] are swapped, making s = "nmo", which shares only "o" with t.
import math # Add any extra import statements you may need here # Add any helper functions you may need here from collections import defaultdict def matching_pairs(s, t): char2count = defaultdict(int) count = 0 unmatched_t = [] for i in range(len(s)): if s[i] == t[i]: count += 1 else: char2count[ s[i] ] += 1 unmatched_t.append( t[i] ) for char in unmatched_t: if char in char2count and char2count[char] > 0: char2count[char] -= 1 count += 1 if len(unmatched_t) < 2: count -= min(2, len(s)) - len(unmatched_t) return count # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": s_1, t_1 = "abcde", "adcbe" expected_1 = 5 output_1 = matching_pairs(s_1, t_1) check(expected_1, output_1) s_2, t_2 = "abcd", "abcd" expected_2 = 2 output_2 = matching_pairs(s_2, t_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
c. Minimum Length Substrings
You are given two strings s and t. You can select any substring of string s and rearrange the characters of the selected substring. Determine the minimum length of the substring of s such that string t is a substring of the selected substring.
Input
s and t are non-empty strings that contain less than 1,000,000 characters each
Output
Return the minimum length of the substring of s. If it is not possible, return -1
Example
s = "dcbefebce"
t = "fd"
output = 5
Explanation:
Substring "dcbef" can be rearranged to "cfdeb", "cefdb", and so on. String t is a substring of "cfdeb". Thus, the minimum length required is 5.
import math def min_length_substring(s, t): for char in t: if char not in s: return -1 if s.count(char) < t.count(char): return -1 indices = [] for idx, char in enumerate(s): if char in t: indices.append(idx) # length of substring is always: distance between chars + 1 dist = max(indices) - min(indices) + 1 return dist # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": s1 = "dcbefebce" t1 = "fd" expected_1 = 5 output_1 = min_length_substring(s1, t1) check(expected_1, output_1) s2 = "bfbeadbcbcbfeaaeefcddcccbbbfaaafdbebedddf" t2 = "cbccfafebccdccebdd" expected_2 = -1 output_2 = min_length_substring(s2, t2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
4. Heap
a. Largest Triple Products
List of n integers arr[0:(n-1)]. Compute list [0..(n-1)] where at each i output[i] = product of 3 largest elements in arr[0:i] (or -1 if i < 2).
The 3 largest elements can have the same values, but different indices.
Input
n is in the range [1, 100,000].
Each value arr[i] is in the range [1, 1,000].
Output Return a list of n integers output[0..(n-1)], as described above.
Example 1
n = 5
arr = [1, 2, 3, 4, 5]
output = [-1, -1, 6, 24, 60]
The 3rd element of output is 321 = 6, the 4th is 432 = 24, and the 5th is 543 = 60.
Example 2
n = 5
arr = [2, 1, 2, 1, 2]
output = [-1, -1, 4, 4, 8]
The 3rd element of output is 221 = 4, the 4th is 221 = 4, and the 5th is 222 = 8.
My idea: Iterate over arr and add elements to max heap (sorted array - see b. Magical Candy Bags for why). Once heap has 3 or more elements, return the product of the last three (max) elements from heap
def insert( arr2, e ): ''' Insert element e into sorted array arr2 to keep arr2 sorted (O(logn) when n=len(arr), not arr2) ''' # EDGE CASES - EMPTY ARRAY OR e LARGER THAN LARGEST ELEMENT IN arr2 if len(arr2) == 0: return [e] elif e >= arr2[-1]: return arr2 + [e] for i,e2 in enumerate(arr2): if e2 > e: return arr2[:i] + [e] + arr2[i:] return arr2 def findMaxProduct(arr): ''' Iterate over arr and add elements to max heap (sorted array) Once heap has 3 or more elements, return the product of the last three (max) elements from heap Time: findMaxProduct = O(n), insert = O(logn). Overall = O(nlogn) ''' sorted_arr = [] res = [] for i,e in enumerate(arr): sorted_arr = insert(sorted_arr, e) #print( 'Current heap:', sorted_arr) if i<2: res.append(-1) else: res.append( (sorted_arr[-3]*sorted_arr[-2])*sorted_arr[-1] ) return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": arr_1 = [1, 2, 3, 4, 5] expected_1 = [-1, -1, 6, 24, 60] output_1 = findMaxProduct(arr_1) check(expected_1, output_1) arr_2 = [2, 4, 7, 1, 5, 3] expected_2 = [-1, -1, 56, 56, 140, 140] output_2 = findMaxProduct(arr_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
c. Magical Candy Bags
Heap Theory
Note: heap sort - O(n) time for building Heap and O(n log n) to remove each node in order = O(n log n)
Insertion operation has a worst-case time complexity of O(log n)
Search in a heap, as it is, will need O(N) time
MinHeap operations * Insertion O(log n): Finding the exact position of the new element is performed in logn since it is only compared with *the position of the parent nodes. * Delete Min O(logn): After the minimum element is removed, the heap has to put the new root in place. * Find Min O(1): This is possible because the heap data structure always has the minimum element on the root node. * Heapify O(n): This operation rearranges all the nodes after deletion or insertion operation. The cost of this operation is nn since all the elements have to be moved to keep the heap properties. * Delete O(logn): A specific element from the heap can be removed in log nlogn time.
If heap is implemented as sorted array: find min/max, delete min/max are O(1), but insertion in a sorted array is not trivial. Given a new element 𝑒, we can use binary search to locate its position in the array to insert it. But the point is that if you want to insert it there, you have to move a lot of old elements (can be O(n)) around to make a vacancy for the new element to reside. This is quite inefficient for most applications. You may also choose to re-sort the array after an element is inserted, this requires O(nlogn) time however. IN GENERAL, VERY ROUGHLY WE CAN APPROXIMATE HEAP IMPLEMENTATION AS SORTED ARRAY! (and may be effecient for a given task)
Binary Heap = Complete Binary Tree => easily represented as an array (space-efficient). If parent at index I, the left child can be calculated by 2 * I + 1 and the right child by 2 * I + 2 (assuming the indexing starts at 0)
Descrption You have N bags of candy. The ith bag contains arr[i] pieces of candy! It takes 1 minute to eat all in one bag, then the bag refills with floor(x/2) pieces (largest int < x). You have k minutes to eat as much candy as possible. How many pieces of candy can you eat?
Input
1 ≤ N ≤ 10,000
1 ≤ k ≤ 10,000
1 ≤ arr[i] ≤ 1,000,000,000
Output A single integer, the maximum number of candies you can eat in k minutes.
Example 1 N = 5 k = 3 arr = [2, 1, 7, 4, 2] output = 14 In the first minute you can eat 7 pieces of candy. That bag will refill with floor(7/2) = 3 pieces. In the second minute you can eat 4 pieces of candy from another bag. That bag will refill with floor(4/2) = 2 pieces. In the third minute you can eat the 3 pieces of candy that have appeared in the first bag that you ate. In total you can eat 7 + 4 + 3 = 14 pieces of candy.
Create heap out of all elements of arr. Then iterate heap k times, each time taking the max value & adding it to result, while also applying floor(x/2) to the max value and inserting it back into correct spot in heap
import math def insert( arr2, e ): ''' Insert element e into sorted array arr2 to keep arr2 sorted (O(logn) when n=len(arr), not arr2) ''' # EDGE CASES - EMPTY ARRAY OR e LARGER THAN LARGEST ELEMENT IN arr2 if len(arr2) == 0: return [e] elif e >= arr2[-1]: return arr2 + [e] for i,e2 in enumerate(arr2): if e2 > e: return arr2[:i] + [e] + arr2[i:] return arr2 def maxCandies(arr, k): # BUILD HEAP heap = [] for e in arr: heap = insert(heap, e) res = 0 for i in range(k): max_value = heap[-1] res += max_value heap = insert(heap[:-1], math.floor( max_value/2 )) return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": n_1, k_1 = 5, 3 arr_1 = [2, 1, 7, 4, 2] expected_1 = 14 output_1 = maxCandies(arr_1, k_1) check(expected_1, output_1) n_2, k_2 = 9, 3 arr_2 = [19, 78, 76, 72, 48, 8, 24, 74, 29] expected_2 = 228 output_2 = maxCandies(arr_2, k_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
c. Median Stream
List of n integers arr[0..(n-1)]. Output: output[0..(n-1)] - for each index i, output[i] = median of arr[0..i] (rounded down to the nearest integer).
Median -
a) middle integer in the sorted order if len(arr) is odd.
b) average of the two middle-most integers, otherwise
Input
n is in the range [1, 1,000,000].
Each value arr[i] is in the range [1, 1,000,000].
Output
Return a list of n integers output[0..(n-1)], as described above.
Example 1
n = 4
arr = [5, 15, 1, 3]
output = [5, 10, 5, 4]
The median of [5] is 5, the median of [5, 15] is (5 + 15) / 2 = 10, the median of [5, 15, 1] is 5, and the median of [5, 15, 1, 3] is (3 + 5) / 2 = 4.
Example 2
n = 2
arr = [1, 2]
output = [1, 1]
The median of [1] is 1, the median of [1, 2] is (1 + 2) / 2 = 1.5 (which should be rounded down to 1).
My idea: heap representation as a sorted array is best for this task! iterate over arr & add them to heap. For each i, I will have a sorted arr[:i] as heap, in which all I need is just to compute the median and add it to result
import math def insert( arr2, e ): ''' Insert element e into sorted array arr2 to keep arr2 sorted (O(logn) when n=len(arr), not arr2) ''' # EDGE CASES - EMPTY ARRAY OR e LARGER THAN LARGEST ELEMENT IN arr2 if len(arr2) == 0: return [e] elif e >= arr2[-1]: return arr2 + [e] for i,e2 in enumerate(arr2): if e2 >= e: return arr2[:i] + [e] + arr2[i:] return arr2 def get_one_median( arr ): ''' Return median value of array arr ''' if len(arr) == 0: return None elif len(arr) == 1: return arr[0] elif len(arr)%2 == 0: idx = len(arr)//2 return math.floor((arr[idx] + arr[idx-1])/2) elif len(arr)%2 != 0: idx = len(arr)//2 return arr[idx] def findMedian(arr): heap, res = [], [] for e in arr: heap = insert(heap, e) res.append( get_one_median(heap) ) return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": arr_1 = [5, 15, 1, 3] expected_1 = [5, 10, 5, 4] output_1 = findMedian(arr_1) check(expected_1, output_1) arr_2 = [2, 4, 7, 1, 5, 3] expected_2 = [2, 3, 4, 3, 4, 3] output_2 = findMedian(arr_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
5. Greedy Algorithms
a. Slow Sums
List of N numbers, repeat this until one is left: choose any two numbers and replace them with their sum. Penalty for this operation = resultant sum. Penalty for entire array - sum of individual penalties. Find max possible penalty.
Input
An array arr containing N integers, denoting the numbers in the list.
Output
int = max possible total penalty
Constraints:
1 ≤ N ≤ 10^6
1 ≤ Ai ≤ 10^7, where Ai = ith initial elem. of array.
The sum of values of N over all test cases will not exceed 5 * 10^6
Example
arr = [4, 2, 1, 3]
output = 26
First, add 4 + 3 for a penalty of 7. Now the array is [7, 2, 1]
Add 7 + 2 for a penalty of 9. Now the array is [9, 1]
Add 9 + 1 for a penalty of 10. The penalties sum to 26.
My idea: to get the max penalty for each indiv. operation - sum up the 2 largest numbers of max heap (=2 last numbers in sorted array), then insert sum into heap[:-2]
import math def insert( arr2, e ): ''' Insert element e into sorted array arr2 to keep arr2 sorted (O(logn) when n=len(arr), not arr2) ''' # EDGE CASES - EMPTY ARRAY OR e LARGER THAN LARGEST ELEMENT IN arr2 if len(arr2) == 0: return [e] elif e >= arr2[-1]: return arr2 + [e] for i,e2 in enumerate(arr2): if e2 > e: return arr2[:i] + [e] + arr2[i:] return arr2 def getTotalTime(arr): # BUILD HEAP heap = [] for e in arr: heap = insert(heap, e) stop, res = False, 0 while not stop: this_sum = heap[-1]+heap[-2] res += this_sum heap = insert( heap[:-2], this_sum ) if len(heap) == 2: res += sum(heap) stop = True return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": arr_1 = [4, 2, 1, 3] expected_1 = 26 output_1 = getTotalTime(arr_1) check(expected_1, output_1) arr_2 = [2, 3, 9, 8, 4] expected_2 = 88 output_2 = getTotalTime(arr_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
b. Element Swapping
Arr of size n; find lexicographically smallest sequence after at most k element swaps of 2 consecutive elements.
Note: x lexicographically < y (equal-length) IFF, for earliest index i where two lists differ, x[i] < y[i].
Input
n is in the range [1, 1000].
Each element of arr is in the range [1, 1,000,000].
k is in the range [1, 1000].
Output
Return an array of n integers output, the lexicographically smallest sequence achievable after at most k swaps.
Example 1
n = 3
k = 2
arr = [5, 3, 1]
output = [1, 5, 3]
We can swap the 2nd and 3rd elements, followed by the 1st and 2nd elements, to end up with the sequence [1, 5, 3]. This is the lexicographically smallest sequence achievable after at most 2 swaps.
Example 2
n = 5
k = 3
arr = [8, 9, 11, 2, 1]
output = [2, 8, 9, 11, 1]
We can swap [11, 2], followed by [9, 2], then [8, 2].
Geeksforgeeks
Getting all permutation of arr and picking the smallest O(n!).
Instead, think greedily. Pick smallest element from arr[:k] (or arr if k > n) and place it at index 0 shifting all other elements right by 1 position. Subtract num of swaps from k - if k > 0, find the next smallest and place it in position 1. And so on until k=0
import math def findMinArray(arr, k): # iterate starting from i=0 n = len(arr) for i in range(n-1): # index of min element to swap with index i idx = i # find min element from i+1 to min(k,n) for j in range(i+1, n): # if exceeding max swaps if j-i > k: break if arr[j] < arr[idx]: idx = j # swap elements for j in range(idx, i, -1): arr[j],arr[j-1] = arr[j-1], arr[j] # set final k after swapping idx-i elements k -= idx - i return arr # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": n_1 = 3 arr_1 = [5, 3, 1] k_1 = 2 expected_1 = [1, 5, 3] output_1 = findMinArray(arr_1,k_1) check(expected_1, output_1) n_2 = 5 arr_2 = [8, 9, 11, 2, 1] k_2 = 3 expected_2 = [2, 8, 9, 11, 1] output_2 = findMinArray(arr_2,k_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
c. Seating Arrangements
Arr of size n - heights of guests in inches sitting at a circular table clockwise (n! possible permutations of seat assignments). Awkwardness between two adjacent guests = absolute difference between their heights. Seats 1 and n are adjacent too (n pairs of adjacent guests).
Overall awkwardness - max awkwardness of any pair of adjacent guests.
Find min possible overall awkwardness.
Input
n is in the range [3, 1000].
Each height arr[i] is in the range [1, 1000].
Output
Return the minimum achievable overall awkwardness of any seating arrangement.
Example
n = 4
arr = [5, 10, 6, 8]
output = 4
Permutation [3, 1, 4, 2] in clockwise order (heights [6, 5, 8, 10]), then the four awkwardnesses between pairs of adjacent guests will be |6-5| = 1, |5-8| = 3, |8-10| = 2, and |10-6| = 4, yielding an overall awkwardness of 4. It's impossible to achieve a smaller overall awkwardness.
1
2 ___ 3
4 / \ 5
6 \___/ 7
8 9
My idea (based on Leetcode discussion): put max in empty new_arr, then put next max to left, next max to right, next max to left, next max to right and so on. This way max will be in middle of new array, and the highest awkwardness will be minimized. Calculate awkwardness for each placed max and find the max awkwardness in the end
import math def minOverallAwkwardness(arr): arr.sort( reverse=True ) # O(nlogn) awks = [] new_arr = [ arr[0] ] for i, e in enumerate(arr[1:]): # O(n) if i%2 == 0: awks.append( abs(e - new_arr[0]) ) new_arr = [e] + new_arr else: awks.append( abs(e - new_arr[-1]) ) new_arr = new_arr + [e] return max(awks) # O(n). Overall, O(nlogn + 2n) = O(nlogn) def minOverallAwkwardness2(arr): arr.sort() # O(nlogn) print(arr) print( arr[min(2,len(arr)-1):] ) return max( ( abs(a-b) for a,b in zip(arr,arr[min(2,len(arr)-1):]) ) ) def minOverallAwkwardness3(arr): arr.sort() # O(nlogn) diff = arr[1] - arr[0] for i in range (2, len(arr), 2): diff = max( diff, arr[i] - arr[i-2]) for i in range (3, len(arr), 2): diff = max( diff, arr[i] - arr[i-2]) return max( diff, arr[ len(arr)-1 ] - arr[ len(arr)-2 ] ) # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": arr_1 = [5, 10, 6, 8] expected_1 = 4 output_1 = minOverallAwkwardness(arr_1) check(expected_1, output_1) arr_2 = [1, 2, 5, 3, 7] expected_2 = 4 output_2 = minOverallAwkwardness(arr_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
6. Trees
a. Number of Visible Nodes
Binary tree w/n nodes. Viewing the tree from left side (= seeing only leftmost nodes at each level), return # visible nodes.
Note: the leftmost node at a level could be a right node, not only left node.
Input The root node of a tree, where the number of nodes is between 1 and 1000, and the value of each node is between 0 and 1,000,000,000
Output An int representing the number of visible nodes.
Example
8 <------ root
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
output = 4
My idea - this just the height (why not (height + 1)?!)
import math class TreeNode: def __init__(self,key): self.left = None self.right = None self.val = key def max_depth(root): if root is None: return 0 else: left_depth = max_depth(root.left) right_depth = max_depth(root.right) if left_depth > right_depth: return left_depth + 1 else: return right_depth + 1 def visible_nodes(root): return max_depth(root) # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": root_1 = TreeNode(8) root_1.left = TreeNode(3) root_1.right = TreeNode(10) root_1.left.left = TreeNode(1) root_1.left.right = TreeNode(6) root_1.left.right.left = TreeNode(4) root_1.left.right.right = TreeNode(7) root_1.right.right = TreeNode(14) root_1.right.right.left = TreeNode(13) expected_1 = 4 output_1 = visible_nodes(root_1) check(expected_1, output_1) root_2 = TreeNode(10) root_2.left = TreeNode(8) root_2.right = TreeNode(15) root_2.left.left = TreeNode(4) root_2.left.left.right = TreeNode(5) root_2.left.left.right.right = TreeNode(6) root_2.right.left =TreeNode(14) root_2.right.right = TreeNode(16) expected_2 = 5 output_2 = visible_nodes(root_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
b. Nodes in a Subtree
Tree of N nodes, each containing int u corresponding to lowercase char c in string s (1-based indexing).
You are required to answer Q queries of type [u, c], where u is an integer and c is a lowercase letter. The query result is the number of nodes in the subtree of node u containing c.
Input
A pointer to the root node, an array list containing Q queries of type [u, c], and a string s
Constraints
N & Q = integers in [1, 1,000,000]
u = unique int in [1 and N]
s = length N, containing only lowercase letters
c is a lowercase letter contained in string s
Node 1 = root
Output
int array with response to each query
Example
1(a)
/ \
2(b) 3(a)
s = "aba"
RootNode = 1
query = [[1, 'a']]
Note: Node 1 corresponds to first letter 'a', Node 2 corresponds to second letter of the string 'b', Node 3 corresponds to third letter of the string 'a'.
output = [2]
Both Node 1 and Node 3 contain 'a', so the number of nodes within the subtree of Node 1 containing 'a' is 2.
import math class Node: def __init__(self, data): self.val = data self.children = [] def find_target(root, node_num): ''' Find the subtree root whose value = node_num ''' if root.val == node_num: return root else: if root.children: for child in root.children: res = find_target(child, node_num) if res is not None: return res def count_value(target, value_c, s): ''' Count # times value_c occurs in subtree of target ''' # base case if target is None: return 0 # check if this node's value = value_c char # then recirsively check the same for all children nodes count = 0 if s[ target.val-1 ] == value_c: # -1 is conversion from 1-based indexing count += 1 if target.children: for child in target.children: count += count_value(child, value_c, s) return count def count_of_nodes(root, queries, s): res = [] for query in queries: # O(n) node_num, value_c = query[0], query[1] target = find_target(root, node_num) # O(n) print('Node:', node_num) if target is not None: print('Found:', target.val) else: print('Found zilch') res.append( count_value(target, value_c, s) ) # O(n). Overall complexity = O(n) return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": # Testcase 1 n_1 ,q_1 = 3, 1 s_1 = "aba" root_1 = Node(1) root_1.children.append(Node(2)) root_1.children.append(Node(3)) queries_1 = [(1, 'a')] output_1 = count_of_nodes(root_1, queries_1, s_1) expected_1 = [2] check(expected_1, output_1) # Testcase 2 n_2 ,q_2 = 7, 3 s_2 = "abaacab" root_2 = Node(1) root_2.children.append(Node(2)) root_2.children.append(Node(3)) root_2.children.append(Node(7)) root_2.children[0].children.append(Node(4)) root_2.children[0].children.append(Node(5)) root_2.children[1].children.append(Node(6)) queries_2 = [(1, 'a'),(2, 'b'),(3, 'a')] output_2 = count_of_nodes(root_2, queries_2, s_2) expected_2 = [4, 1, 2] check(expected_2, output_2) # Add your own test cases here
Node: 1 Found: 1 ✓Test #1 Node: 1 Found: 1 Node: 2 Found: 2 Node: 3 Found: 3 ✓Test #2
7. Search
a. Revenue Milestones
Array of daily revenues + array of milestones = return array with days when each milestone was reachd.
Input
revenues is a length-N array representing how much revenue FB made on each day (from day 1 to day N). milestones is a length-K array of total revenue milestones.
Output
Return a length-K array where K_i is the day on which FB first had milestones[i] total revenue. If the milestone is never met, return -1.
Example
revenues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
milestones = [100, 200, 500]
output = [4, 6, 10]
Explanation
On days 4, 5, and 6, FB has total revenue of $100, $150, and $210 respectively. Day 6 is the first time that FB has >= $200 of total revenue.
import math # Add any extra import statements you may need here # Add any helper functions you may need here def get_index( arr, target ): for idx, item in enumerate(arr): if item >= target: return idx return -1 def get_index2( arr, target ): lo, hi = 0, len(arr) while lo < hi: mid = (lo + hi) // 2 if arr[mid] >= target: hi = mid else: lo = mid + 1 return lo def getMilestoneDays(revenues, milestones): ''' get_index(): O(r) + O(r)*O(m) get_index2(): O(r) + O(logr)*O(m) ''' res = [] for i in range(1, len(revenues) ): #O(r) revenues[i] += revenues[i-1] for i, m in enumerate(milestones): #O(m) day = get_index2(revenues, m) #O(r) or O(logr) if day != -1: day += 1 res.append( day ) return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": revenues_1 = [100, 200, 300, 400, 500] milestones_1 = [300, 800, 1000, 1400] expected_1 = [2, 4, 4, 5] output_1 = getMilestoneDays(revenues_1, milestones_1) check(expected_1, output_1) revenues_2 = [700, 800, 600, 400, 600, 700] milestones_2 = [3100, 2200, 800, 2100, 1000] expected_2 = [5, 4, 2, 3, 2] output_2 = getMilestoneDays(revenues_2, milestones_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
b. 1 Billion Users
N apps with user growth rates (g).
On day t, # users = g^t (fractions OK). Apps launched simultaneously, no user uses than one apps.
After how many full days will we have 1 billion total users across all the N apps?
Input
1.0 < growthRate < 2.0 for all growth rates
1 <= N <= 1,000
Output
Return the number of full days it will take before we have a total of 1 billion users across all N apps.
Example 1
growthRates = [1.5]
output = 52
Example 2
growthRates = [1.1, 1.2, 1.3]
output = 79
Example 3
growthRates = [1.01, 1.02]
output = 1047
import math def find_day(lo_val, hi_val, count, growthRates): # build arrays (can we exclude this step?) arr = [] lo = 2**(count-1) hi = 2**count for j in range(lo, hi+1): arr.append( sum([item**j for item in growthRates]) ) # binary search target = 1000000000 lo = 0 hi = len(arr) while lo < hi: mid = (lo + hi)//2 if arr[mid] >= target: hi = mid else: lo = mid + 1 return lo + 2**(count-1) def getBillionUsersDay(growthRates): ''' O(log(days)) time complexity ''' count, target = 0, 1000000000 while True: # O(log(days)) i = 2**count total = sum([item**i for item in growthRates]) if total > target: break count += 1 days = find_day( sum([item**(2**(count-1)) for item in growthRates]), total, count, growthRates ) return days def getBillionUsersDay2(growthRates): ''' O(days) time complexity ''' total, days = 0, 0 while total < 1000000000: days += 1 total = sum([item**days for item in growthRates]) return days # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": test_1 = [1.1, 1.2, 1.3] expected_1 = 79 output_1 = getBillionUsersDay(test_1) check(expected_1, output_1) test_2 = [1.01, 1.02] expected_2 = 1047 output_2 = getBillionUsersDay(test_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
8. Sorting
a. Balanced Split
Array of [repeated] integers - split it into A & B subarrays w/equal sums + all int in A are strictly smaller than ints in B (every int in A is less than, and not equal to, every int in B).
Input
All integers in array are in the range [0, 1,000,000,000].
Output
Return true if such a split is possible, and false otherwise.
Example 1
arr = [1, 5, 7, 1]
output = true
We can split the array into A = [1, 1, 5] and B = [7].
Example 2
arr = [12, 7, 6, 7, 6]
output = false
We can't split the array into A = [6, 6, 7] and B = [7, 12] since this doesn't satisfy the requirement that all integers in A are smaller than all integers in B.
import math def balancedSplitExists(arr): ''' Classical O(n) solution for balanced sum + keeping minRight as the minimum int in right subarray to check the left subarray against it. STILL O(N)!!! ''' # traverse left to right to get sum leftSum = 0 for item in arr: #O(n) leftSum += item # traverse right to left to compare leftSum and rightSum + keep minRight rightSum = 0 minRight = 1000000001 for i in range(len(arr)-1, -1, -1): #O(n) if arr[i] < minRight: minRight = arr[i] rightSum += arr[i] leftSum -= arr[i] if rightSum == leftSum: check_less = True for item in arr[:i]: #O(n) if item >= minRight: check_less = False if check_less: return True return False # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printString(string): print('[\"', string, '\"]', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printString(expected) print(' Your output: ', end='') printString(output) print() test_case_number += 1 if __name__ == "__main__": arr_1 = [2, 1, 2, 5] expected_1 = True output_1 = balancedSplitExists(arr_1) check(expected_1, output_1) arr_2 = [3, 6, 3, 4, 4] expected_2 = False output_2 = balancedSplitExists(arr_2) check(expected_2, output_2) # Add your own test cases here arr_3 = [5, 5, 5, 8, 7] expected_3 = True output_3 = balancedSplitExists(arr_3) check(expected_3, output_3)
✓Test #1 ✓Test #2 ✓Test #3
b. Counting Triangles
List of N triangles (side lengths) - how many distinct triangles are there are? (Same = can be placed on the plane and vertices coincide).
Input
List of triplets. It's guaranteed that all triplets of side lengths represent real triangles.
All side lengths are in the range [1, 1,000,000,000]
1 <= N <= 1,000,000
Output
Number of distinct triangles in the list.
Example 1
arr = [[2, 2, 3], [3, 2, 2], [2, 5, 6]]
output = 2
The first two triangles are the same, so there are only 2 distinct triangles.
Example 2
arr = [[8, 4, 6], [100, 101, 102], [84, 93, 173]]
output = 3
All of these triangles are distinct.
Example 3
arr = [[5, 8, 9], [5, 9, 8], [9, 5, 8], [9, 8, 5], [8, 9, 5], [8, 5, 9]]
output = 1
All of these triangles are the same.
Mi idea: same triangle = same sides in a different order. Iterate over triples and put them into a dict: key=sum of triple, values=list of frozensets of triples. Iterate again and count unique triples for each key! Complexity should be O(n)
Sorting will add to complexity making it O(nlogn)
import math from collections import defaultdict def countDistinctTriangles(arr): ''' O(n) ''' res = defaultdict(list) for i in arr: # O(n) s = sum(i) res[s].append(frozenset(i)) count = 0 for i in res: # O(n) count += len(set(res[i])) return count def countDistinctTriangles2(arr): '''O(n)?''' arr = set([frozenset(i) for i in arr]) return len(arr) def countDistinctTriangles3(arr): ''' O(nlogn) with sorting''' arr = [tuple(sorted(list(i))) for i in arr] # O(nlogn) arr = set(arr) return len(arr) # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": arr_1 = [(7, 6, 5), (5, 7, 6), (8, 2, 9), (2, 3, 4), (2, 4, 3)] expected_1 = 3 output_1 = countDistinctTriangles(arr_1) check(expected_1, output_1) arr_2 = [(3, 4, 5), (8, 8, 9), (7, 7, 7)] expected_2 = 3 output_2 = countDistinctTriangles(arr_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
9. Recursion
Encrypted Words
Encryption method: * Start with empty string * Append mid char of original string S (left-most central char if even length) * Append encrypted substring of S on the left of mid char (if non-empty) * Append encrypted substring of S on the right of mid char (if non-empty)
"abc" => take "b", append encrypted "a" (just "a"), append encrypted "c" (just "c") = "bac"
"abcxcba" => take "x" + encrypted "abc" + encrypted "cba" = "xbacbca"
Input
S contains only lower-case alphabetic characters
1 <= |S| <= 10,000
Output
Return string R, the encrypted version of S.
Example 1
S = "abc"
R = "bac"
Example 2
S = "abcd"
R = "bacd"
Example 3
S = "abcxcba"
R = "xbacbca"
Example 4
S = "facebook"
R = "eafcobok"
import math def findEncryptedWord(s): if len(s) == 0: return '' if len(s)%2 == 0: mid = math.floor(len(s)/2)-1 else: mid = math.ceil(len(s)/2)-1 return s[mid] + findEncryptedWord(s[:mid]) + findEncryptedWord(s[mid+1:]) # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printString(string): print('[\"', string, '\"]', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printString(expected) print(' Your output: ', end='') printString(output) print() test_case_number += 1 if __name__ == "__main__": s1 = "abc" expected_1 = "bac" output_1 = findEncryptedWord(s1) check(expected_1, output_1) s2 = "abcd" expected_2 = "bacd" output_2 = findEncryptedWord(s2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
b. Change in a Foreign Currency
You likely know that different currencies have coins and bills of different denominations. In some currencies, it's actually impossible to receive change for a given amount of money. For example, Canada has given up the 1-cent penny. If you're owed 94 cents in Canada, a shopkeeper will graciously supply you with 95 cents instead since there exists a 5-cent coin. Given a list of the available denominations, determine if it's possible to receive exact change for an amount of money targetMoney. Both the denominations and target amount will be given in generic units of that currency.
Signature
boolean canGetExactChange(int targetMoney, int[] denominations)
Input
1 ≤ |denominations| ≤ 100
1 ≤ denominations[i] ≤ 10,000
1 ≤ targetMoney ≤ 1,000,000
Output
Return true if it's possible to receive exactly targetMoney given the available denominations, and false if not.
Example 1
denominations = [5, 10, 25, 100, 200]
targetMoney = 94
output = false
Every denomination is a multiple of 5, so you can't receive exactly 94 units of money in this currency.
Example 2
denominations = [4, 17, 29]
targetMoney = 75
output = true
You can make 75 units with the denominations [17, 29, 29].
Idea: using the DP solution, return True if there is a solution
import math def canGetExactChange(target, arr): ''' DP solution O(m*n) ''' # table[i] = # solutions for value i. n+1 entris because base case n = 0 table = [0 for k in range(target+1)] # Base case (If given value is 0) table[0] = 1 for i in range(0,len(arr)): for j in range(arr[i],target+1): table[j] += table[j-arr[i]] return True if table[target] else False # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printString(string): print('[\"', string, '\"]', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printString(expected) print(' Your output: ', end='') printString(output) print() test_case_number += 1 if __name__ == "__main__": target_1 = 94 arr_1 = [5, 10, 25, 100, 200] expected_1 = False output_1 = canGetExactChange(target_1, arr_1) check(expected_1, output_1) target_2 = 75 arr_2 = [4, 17, 29] expected_2 = True output_2 = canGetExactChange(target_2, arr_2) check(expected_2, output_2) # Add your own test cases here
✗Test #1: Expected ["False"] Your output: ["True"] ✓Test #2
10. Stack
a. Balance Brackets
Brackets: '(){}[]'. Matching: open-bracket + close-bracket of the same type. Additional conditions: * The sequence is empty, or * The sequence has two or more non-empty balanced sequences, or * The first and last brackets are matching, and the subsequence inside is balanced.
Input
String s with length between 1 and 1000
Output
A boolean representing if the string is balanced or not
Example 1
s = {[()]}
output: true
Example 2
s = {}()
output: true
Example 3
s = {(})
output: false
Example 4
s = )
output: false
import math # Add any extra import statements you may need here # Add any helper functions you may need here def isBalanced(s): if len(s) == 0: return True opening = '{[(' pairs = [('(', ')'), ('[', ']'), ('{','}')] stack = [] for char in s: if char in opening: stack.append(char) else: if len(stack) == 0: return False last = stack.pop() if (last, char) not in pairs: return False return len(stack) == 0 # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printString(string): print('[\"', string, '\"]', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printString(expected) print(' Your output: ', end='') printString(output) print() test_case_number += 1 if __name__ == "__main__": s1 = "{[(])}" expected_1 = False output_1 = isBalanced(s1) check(expected_1, output_1) s2 = "{{[[(())]]}}" expected_2 = True output_2 = isBalanced(s2) check(expected_2, output_2) # Add your own test cases here s3 = "{{[[()]]}}" expected_3 = True output_3 = isBalanced(s3) check(expected_3, output_3) s4 = "{[()]}" expected_4 = True output_4 = isBalanced(s4) check(expected_4, output_4)
✓Test #1 ✓Test #2 ✓Test #3 ✓Test #4
Queue
a. Queue Removals
You're given a list of n integers arr, which represent elements in a queue (in order from front to back). You're also given an integer x, and must perform x iterations of the following 3-step process: Pop x elements from the front of queue (or, if it contains fewer than x elements, pop all of them) Of the elements that were popped, find the one with the largest value (if there are multiple such elements, take the one which had been popped the earliest), and remove it For each one of the remaining elements that were popped (in the order they had been popped), decrement its value by 1 if it's positive (otherwise, if its value is 0, then it's left unchanged), and then add it back to the queue Compute a list of x integers output, the ith of which is the 1-based index in the original array of the element which had been removed in step 2 during the ith iteration.
Input x is in the range [1, 316]. n is in the range [x, x*x]. Each value arr[i] is in the range [1, x]. Output Return a list of x integers output, as described above. Example n = 6 arr = [1, 2, 2, 3, 4, 5] x = 5 output = [5, 6, 4, 1, 2] The initial queue is [1, 2, 2, 3, 4, 5] (from front to back). In the first iteration, the first 5 elements are popped off the queue, leaving just [5]. Of the popped elements, the largest one is the 4, which was at index 5 in the original array. The remaining elements are then decremented and added back onto the queue, whose contents are then [5, 0, 1, 1, 2]. In the second iteration, all 5 elements are popped off the queue. The largest one is the 5, which was at index 6 in the original array. The remaining elements are then decremented (aside from the 0) and added back onto the queue, whose contents are then [0, 0, 0, 1]. In the third iteration, all 4 elements are popped off the queue. The largest one is the 1, which had the initial value of 3 at index 4 in the original array. The remaining elements are added back onto the queue, whose contents are then [0, 0, 0]. In the fourth iteration, all 3 elements are popped off the queue. Since they all have an equal value, we remove the one that was popped first, which had the initial value of 1 at index 1 in the original array. The remaining elements are added back onto the queue, whose contents are then [0, 0]. In the final iteration, both elements are popped off the queue. We remove the one that was popped first, which had the initial value of 2 at index 2 in the o
import math # Add any extra import statements you may need here # Add any helper functions you may need here def get_max(l): max_idx = 0 for i, e in l: if e > l[max_idx]: max_idx = i max_excluded = l[max_idx] l = [item for idx, item in enumerate(l) if idx != max_idx] return l, max_excluded def findPositions(arr, x): # arr_1 = [1, 2, 2, 3, 4, 5], x=5, ouput = [5, 6, 4, 1, 2] res = [] arr = list(zip(arr, range(1, len(arr)+1))) for _ in range(x): if x < len(arr): left = arr[:x] right = arr[x:] else: left = arr right = [] maxi = left[0] for e in left: if e[0] > maxi[0]: maxi = e left = [ item for item in left if item != maxi ] #for i, e in enumerate(left): # if e[0] > 0: # new = e[0] - 1 # else: # new = e[0] # left[i] = (new, e[1]) left = [(a-1, b) if a>0 else (a,b) for a,b in left] res.append(maxi[1]) arr = right + left return res # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') def printIntegerList(array): size = len(array) print('[', end='') for i in range(size): if i != 0: print(', ', end='') print(array[i], end='') print(']', end='') test_case_number = 1 def check(expected, output): global test_case_number expected_size = len(expected) output_size = len(output) result = True if expected_size != output_size: result = False for i in range(min(expected_size, output_size)): result &= (output[i] == expected[i]) rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printIntegerList(expected) print(' Your output: ', end='') printIntegerList(output) print() test_case_number += 1 if __name__ == "__main__": n_1 = 6 x_1 = 5 arr_1 = [1, 2, 2, 3, 4, 5] expected_1 = [5, 6, 4, 1, 2] output_1 = findPositions(arr_1, x_1) check(expected_1, output_1) n_2 = 13 x_2 = 4 arr_2 = [2, 4, 2, 4, 3, 1, 2, 2, 3, 4, 3, 4, 4] expected_2 = [2, 5, 10, 13] output_2 = findPositions(arr_2, x_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
Linked Lists
a. Reverse Operations
- Singly-linked list of N integers. Subpart = contiguous set of even elements, bordered either by either end of the list or an odd element. E.g. in [1, 2, 8, 9, 12, 16], the subparts are [2, 8] and [12, 16].
- List is modified - order of elements in each supart is reversed => [1, 8, 2, 9, 16, 12].
- Determine the original order of the elements.
Must use:
class Node { int data; Node next; }
Constraints
1 <= N <= 1000, where N is the size of the list
1 <= Li <= 10^9, where Li is the ith element of the list
Example
Input:
N = 6
list = [1, 2, 8, 9, 12, 16]
Output:
[1, 8, 2, 9, 16, 12]
import math # Add any extra import statements you may need here class Node: def __init__(self, x): self.data = x self.next = None def reverse(head): node = head evens = [] while node is not None: is_even = node.data % 2 == 0 if is_even: evens.append(node) if not is_even or node.next is None: # not even, start popping while len(evens) > 1: evens[0].data, evens[-1].data = evens[-1].data, evens[0].data evens.pop(0) evens.pop(-1) evens.clear() node = node.next return head def printLinkedList(head): print('[', end='') while head != None: print(head.data, end='') head = head.next if head != None: print(' ', end='') print(']', end='') test_case_number = 1 def check(expectedHead, outputHead): global test_case_number tempExpectedHead = expectedHead tempOutputHead = outputHead result = True while expectedHead != None and outputHead != None: result &= (expectedHead.data == outputHead.data) expectedHead = expectedHead.next outputHead = outputHead.next if not(outputHead == None and expectedHead == None): result = False rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, ' Test #', test_case_number, sep='') else: print(wrongTick, ' Test #', test_case_number, ': Expected ', sep='', end='') printLinkedList(tempExpectedHead) print(' Your output: ', end='') printLinkedList(tempOutputHead) print() test_case_number += 1 def createLinkedList(arr): head = None tempHead = head for v in arr: if head == None: head = Node(v) tempHead = head else: head.next = Node(v) head = head.next return tempHead if __name__ == "__main__": head_1 = createLinkedList([1, 2, 8, 9, 12, 16]) expected_1 = createLinkedList([1, 8, 2, 9, 16, 12]) output_1 = reverse(head_1) check(expected_1, output_1) head_2 = createLinkedList([2, 18, 24, 3, 5, 7, 9, 6, 12]) expected_2 = createLinkedList([24, 18, 2, 3, 5, 7, 9, 12, 6]) output_2 = reverse(head_2) check(expected_2, output_2) # Add your own test cases here
✓ Test #1 ✓ Test #2
Graphs
a. Minimizing Permutations
- Given: int N, permutation P of int from 1 to N, denoted as (a_1, a_2, ..., a_N).
- Rearrange permutation into increasing order by repeating this: Select a sub-portion (a_i, ..., a_j) and reverse it.
- Compute the min # such operations to sort list in increasing order.
Input
arr of all ints from 1 to N, where 1 <= N <= 8
Output
Min # operations
Example
If N = 3, and P = (3, 1, 2), we can do the following operations:
Select (1, 2) and reverse it: P = (3, 2, 1).
Select (3, 2, 1) and reverse it: P = (1, 2, 3).
output = 2
import math import collections def minOperations(arr): ''' BFS solution ''' target = "".join([str(num) for num in sorted(arr)]) curr = "".join([str(num) for num in arr]) queue = collections.deque([(0, curr)]) # queue = [ (<level>, <permutation>),... ] visited = set([curr]) while queue: level, curr = queue.popleft() if curr == target: return level # We are done for i in range(len(curr)): for j in range(i, len(curr)): # Reverse elements between i and j (inclusive) permutation = curr[:i] + curr[ i:j+1 ][::-1] + curr[ j+1: ] if permutation not in visited: visited.add(permutation) queue.append((level + 1, permutation)) return -1 # These are the tests we use to determine if the solution is correct. # You can add your own at the bottom. def printInteger(n): print('[', n, ']', sep='', end='') test_case_number = 1 def check(expected, output): global test_case_number result = False if expected == output: result = True rightTick = '\u2713' wrongTick = '\u2717' if result: print(rightTick, 'Test #', test_case_number, sep='') else: print(wrongTick, 'Test #', test_case_number, ': Expected ', sep='', end='') printInteger(expected) print(' Your output: ', end='') printInteger(output) print() test_case_number += 1 if __name__ == "__main__": n_1 = 5 arr_1 = [1, 2, 5, 4, 3] expected_1 = 1 output_1 = minOperations(arr_1) check(expected_1, output_1) n_2 = 3 arr_2 = [3, 1, 2] expected_2 = 2 output_2 = minOperations(arr_2) check(expected_2, output_2) # Add your own test cases here
✓Test #1 ✓Test #2
Cafeteria (took a while)
A cafeteria table consists of a row of N seats, numbered from 1 to N from left to right. Social distancing guidelines require that every diner be seated such that K seats to their left and K seats to their right (or all the remaining seats to that side if there are fewer than K) remain empty. There are currently M diners seated at the table, the i-th of whom is in seat S_i. No two diners are sitting in the same seat, and the social distancing guidelines are satisfied.
Determine the maximum number of additional diners who can potentially sit at the table without social distancing guidelines being violated for any new or existing diners, assuming that the existing diners cannot move and that the additional diners will cooperate to maximize how many of them can sit down. Please take care to write a solution which runs within the time limit.
Constraints 1≤N≤10^15 N1≤K≤N 1≤M≤500,000 N1≤S_i≤N
Sample Test Case #1 N = 10 K = 1 M = 2 S = [2, 6] Expected Return Value = 3
Sample Test Case #2 N = 15 K = 2 M = 3 S = [11, 6, 14] Expected Return Value = 1
Sample Explanation In the first case, the cafeteria table has N = 10 seats, with two diners currently at seats 2 and 6 respectively. The table initially looks as follows, with brackets covering the K = 1 seat to the left and right of each existing diner that may not be taken. 1 2 3 4 5 6 7 8 9 10 [ ] [ ] Three additional diners may sit at seats 4, 8, and 10 without violating the social distancing guidelines. In the second case, only 1 additional diner is able to join the table, by sitting in any of the first 3 seats.
from typing import List import math def getMaxAdditionalDinersCount(N: int, K: int, M: int, S: List[int]) -> int: S.sort() res = 0 start = 1 for i in S: range_ = i - start res += math.floor( range_/(K+1) ) # floor because we are between 2 people & start = i + K + 1 start = i + K + 1 range_ = N - start + 1 res += math.ceil( range_/(K+1) ) # there may be no one on the right, hence ceiling return res
Director of Photography (my own O(n) solution! Passed all tests)
Note: Chapter 2 is a harder version of this puzzle (larger constraint on N). A photography set consists of N cells in a row, numbered from 1 to N in order, and can be represented by a string C of length N. Each cell i is one of the following types: If C[i] = “P”, cell is allowed to contain a photographer If C[i] = “A”, it is allowed to contain an actor If C[i] = “B”, it is allowed to contain a backdrop If C[i] = “.”, it must be left empty
Photograph = photographer, actor, backdrop, such that the actor is between the photographer and the backdrop. Artistic photograph = distance between photographer and actor is between X and Y cells (inclusive), and distance between actor and backdrop is also between X and Y cells (inclusive). The distance between cells i and j is |i - j| (absolute difference). Determine the number of different artistic photographs which could potentially be taken at the set. Two photographs are considered different if they involve a different photographer cell, actor cell, and/or backdrop cell. 1≤N≤200 1≤X≤Y≤N
Sample Test Case #1 N = 5 C = APABA X = 1 Y = 2 Expected Return Value = 1 Sample Test Case #2 N = 5 C = APABA X = 2 Y = 3 Expected Return Value = 0 Sample Test Case #3 N = 8 C = .PBAAP.B X = 1 Y = 3 Expected Return Value = 3
Sample Explanation In the first case, the absolute distances between photographer/actor and actor/backdrop must be between 11 and 22. The only possible photograph that can be taken is with the 33 middle cells, and it happens to be artistic. In the second case, the only possible photograph is again taken with the 33 middle cells. However, as the distance requirement is between 22 and 33, it is not possible to take an artistic photograph. In the third case, there are 44 possible photographs, illustrated as follows: .P.A...B .P..A..B ..BA.P.. ..B.AP.. All are artistic except the first, where the artist and backdrop exceed the maximum distance of 33.
def getArtisticPhotographCount(N: int, C: str, X: int, Y: int) -> int: #candidates candidates = [] c1, c2, c3 = [], [], [] for idx, char in enumerate( C ): if char == 'P': c1.append([idx,]) print(idx, 'P', c1) elif char == 'A' and c1: c2 += [i+[idx] for i in c1] print(idx, 'A', c1, c2) elif char == 'B' and c2: c3 += [i+[idx] for i in c2] print(idx, 'B', c1, c2, c3) candidates.extend(c3) c1, c2, c3 = [], [], [] for idx, char in enumerate( C ): if char == 'B': c1.append([idx,]) print(idx, 'P', c1) elif char == 'A' and c1: c2 += [i+[idx] for i in c1] print(idx, 'A', c1, c2) elif char == 'P' and c2: c3 += [i+[idx] for i in c2] print(idx, 'B', c1, c2, c3) candidates.extend(c3) candidates = set([tuple(i) for i in candidates]) print(candidates) res = 0 for c in candidates: if (X <= abs(c[1]-c[0]) <= Y) and (X <= abs(c[2]-c[1]) <= Y): res += 1 return res
N = 5 C = 'APABA' X = 1 Y = 2 getArtisticPhotographCount(N, C, X, Y)
1 P [[1]]
2 A [[1]] [[1, 2]]
3 B [[1]] [[1, 2]] [[1, 2, 3]]
4 A [[1]] [[1, 2], [1, 4]]
{(1, 2, 3)}
1Kaitenzushi
Solution - using K+1 sliding window. Solution is correct, but Test Case #2 below is wrongly composed by Facebook - the expected return value should be 5, not 4 (submitted a bug report)
There are N dishes in a row on a kaiten belt, with the i-th dish being of type D_i. Some dishes may be of the same type as one another. In the N dishes, you'll eat a dish as long as it isn't the same type as any of the previous K dishes. You eat very fast, so you can consume a dish before the next one gets to you.
Determine how many dishes you'll end up eating.
Constraints 1≤N≤500,000 1≤K≤N 1≤D[i]≤1,000,000
Sample Test Case #1 N = 6 D = [1, 2, 3, 3, 2, 1] K = 1 Expected Return Value = 5
Sample Test Case #2 N = 6 D = [1, 2, 3, 3, 2, 1] K = 2 Expected Return Value = 4
Sample Test Case #3 N = 7 D = [1, 2, 1, 2, 1, 2, 1] K = 2 Expected Return Value = 2
Sample Explanation In the first case, the dishes have types of [1, 2, 3, 3, 2, 1], so you'll eat the first 3 dishes, skip the next one as it's another type-33 dish, and then eat the last 22.
In the second case, you won't eat a dish if it has the same type as either of the previous 2 dishes you've eaten. After eating the first, second, and third dishes, you'll skip the fourth and fifth dishes as they're the same type as the last 2 dishes that you've eaten. You'll then eat the last dish, consuming 4 dishes total.
In the third case, once you eat the first two dishes you won't eat any of the remaining dishes.
Resume: keep an array element if it hasn't been seen in the last K elements
from typing import List def getMaximumEatenDishCount(N: int, D: List[int], K: int) -> int: res = len(set(D[:K])) i,j = 0, K while j < len(D): if D[j] not in D[i:j]: res += 1 i += 1 j += 1 return res
K = 2 N = 7 D = [1, 2, 1, 2, 1, 2, 1] getMaximumEatenDishCount(N, D, K)
2
Rotary Lock (Chapter 1)
You're trying to open a lock. The lock comes with a wheel which has the integers from 1 to N arranged in a circle in order around it (with integers 1 and N adjacent to one another). The wheel is initially pointing at 1. For example, the following depicts the lock for N = 10 (as is presented in the second sample case).
It takes 1 second to rotate the wheel by 1 unit to an adjacent integer in either direction, and it takes no time to select an integer once the wheel is pointing at it. The lock will open if you enter a certain code. The code consists of a sequence of M integers, the iith of which is C_i.
Determine the minimum number of seconds required to select all M of the code's integers in order.
Constraints 3≤N≤50,000,000 1≤M≤1,000 1≤C[i]≤N
Sample Test Case #1 N = 3 M = 3 C = [1, 2, 3] Expected Return Value = 2 Sample Test Case #2 N = 10 M = 4 C = [9, 4, 4, 8] Expected Return Value = 11
Sample Explanation In the first case, there are 3 integers on the lock, and the sequence of integers to be selected is [1, 2, 3]. One optimal way to enter the code is: select 1 \rightarrow→ rotate to 2 (1 second) \rightarrow→ select 2 \rightarrow→ rotate to 3 (1 second) \rightarrow→ select 3. The total time taken is 1 + 1 = 2 seconds. In the second case, the lock consists of the integers 1 through 10, and the sequence to be selected is [9, 4, 4, 8]. One optimal way to enter the code is: rotate from 1 backwards to 9 (2 seconds) \rightarrow→ select 9 \rightarrow→ rotate forwards to 4 (5 seconds) \rightarrow→ select 4 twice \rightarrow→ rotate forwards to 8 (4 seconds) \rightarrow→ select 8. The total time taken is 2 + 5 + 4 = 11 seconds.
from typing import List def rotate_left(N, a, b): if a < b: return abs(a + N - b) elif a > b: return abs(a - b) else: return 0 def rotate_right(N, a, b): if a < b: return abs(b - a) elif a > b: return abs(b + N - a) else: return 0 def getMinCodeEntryTime(N: int, M: int, C: List[int]) -> int: res = 0 C.insert(0,1) for i in range( len(C)-1 ): res += min( rotate_left(N, C[i], C[i+1]), rotate_right(N, C[i], C[i+1]) ) return res
N = 10 M = 4 C = [9, 4, 4, 8] getMinCodeEntryTime(N, M, C)
11
N = 3 M = 3 C = [1, 2, 3] getMinCodeEntryTime(N, M, C)
2
Scoreboard Inference
You are spectating a programming contest with NN competitors, each trying to independently solve the same set of programming problems. Each problem has a point value, which is either 1 or 2.
On the scoreboard, you observe that the i-th competitor has attained a score of S_i, which is a positive integer equal to the sum of the point values of all the problems they have solved.
The scoreboard does not display the number of problems in the contest, nor their point values. Using the information available, you would like to determine the minimum possible number of problems in the contest.
Constraints 1≤N≤500,000 1≤S[i]≤1,000,000,000
Sample Test Case #1 N = 6 S = [1, 2, 3, 4, 5, 6] Expected Return Value = 4
Sample Test Case #2 N = 4 S = [4, 3, 3, 4] Expected Return Value = 3
Sample Test Case #3 N = 4 S = [2, 4, 6, 8] Expected Return Value = 4
Sample Explanation
In the first case, it's possible that there are as few as 4 problems in the contest, for example with point values [1, 1, 2, 2]. The 6 competitors could have solved the following subsets of problems:
1. Problem 1 (1 point)
2. Problem 3 (2 points)
3. Problems 2 and 3 (1 + 2 = 3 points)
4. Problems 1, 2, and 4 (1 + 1 + 2 = 4 points)
5. Problems 2, 3, and 4 (1 + 2 + 2 = 5 points)
6. All 4 problems (1 + 1 + 2 + 2 = 6 points)
It is impossible for all 6 competitors to have achieved their scores if there are fewer than 4 problems.
In the second case, one optimal set of point values is [1, 1, 2].
In the third case, one optimal set of point values is [2, 2, 2, 2].
Intuition behind my solution below:
This seems to be a variant of the coin change problem, but no need to for dynamic programming here because there are only 2 coins: 1 and 2, which correlates with number being odd and even. Min num of problems for an even score: score/2, while for an odd score: score//2 + 1. If there is at least one odd score, we still need at least one 1-scored problem, so the min num will be maximum score//2 plus one 1 1-score. Hence, the final solution
from typing import List def getMinProblemCount(N: int, S: List[int]) -> int: max_ = max(S) have_odd = False for i in S: if i%2 != 0: have_odd = True break if have_odd: return max_//2 + 1 else: return max_//2
coins = [1, 2] N = 6 S = [1, 2, 3, 4, 5, 6] print( getMinProblemCount(N, S) ) N = 4 S = [4, 3, 3, 4] print( getMinProblemCount(N, S) ) N = 4 S = [2, 4, 6, 8] print( getMinProblemCount(N, S) )
4 3 4
Stack Stabilization
Stack of N inflatable discs, with the i-th disc from top having initial radius of R_i inches. Stack unstable - if includes at least one disc whose radius >= that of disc beneath it. => Stack stable - each disc has strictly smaller radius than the disc beneath it.
To make stack stable - repeatedly choose any disc and decrease (deflate) it to ANY radius strictly smaller than the disc’s prior radius (still a positive integer).
Find min num discs to deflate to make stack stable or −1.
Constraints 1≤N≤50 1≤R[i]≤1,000,000,000
Sample Test Case #1 N = 5 R = [2, 5, 3, 6, 5] Expected Return Value = 3
Sample Test Case #2 N = 3 R = [100, 100, 100] Expected Return Value = 2
Sample Test Case #3 N = 4 R = [6, 5, 4, 3] Expected Return Value = -1
Sample Explanation In the first case, the discs (from top to bottom) have radii of [2", 5", 3", 6", 5"]. Optimal solution: deflate disc 1 from 2" to 1", disc 2 from 5" to 2", and disc 4 from 6" to 4" => [1", 2", 3", 4", 5"].
In the second case, deflate disc 1 from 100" to 1" and disc 2 from 100" to 10".
In the third case, it is impossible to make the stack stable after any number of deflations.
My solution below was accepted at the Facebook Recruiting Protal. However, I think it misses one more possibility - when all array elements before the last one can be aligned in an increasing order except for the last element, we can still get the optimal solution by making the last element greater than the next to the last one. My solution doesn't attempt to increase the last element, but it's still passes
from typing import List # Write any import statements here def getMinimumDeflatedDiscCount(N: int, R: List[int]) -> int: if R[-1] < len(R): return -1 count = 0 for i in range(len(R)-2, -1, -1): if R[i] >= R[i+1]: R[i] = R[i+1] - 1 if R[i] < 1: return -1 count += 1 return count
# Expected Return Value = 3 N = 5 R = [2, 5, 3, 6, 5] print( getMinimumDeflatedDiscCount(N, R) ) # Expected Return Value = 2 N = 3 R = [100, 100, 100] print( getMinimumDeflatedDiscCount(N, R) ) # Expected Return Value = -1 N = 4 R = [6, 5, 4, 3] print( getMinimumDeflatedDiscCount(N, R) )
3 2 -1
Uniform Integers
Positive int uniform if all digits are equal. E.g. 222222 is uniform; 223223 is not. Given two positive ints A and B, determine num of uniform ints between A and B, inclusive
Constraints 1≤A≤B≤10^12
Sample Test Case #1 A = 75 B = 300 Expected Return Value = 5
Sample Test Case #2 A = 1 B = 9 Expected Return Value = 9
Sample Test Case #3 A = 999999999999 B = 999999999999 Expected Return Value = 1
Sample Explanation * In the first case, the uniform integers between 75 and 300 are 77, 88, 99, 111, and 222. * In the second case, all 9 single-digit integers between 1 and 9 (inclusive) are uniform. * In the third case, the single integer under consideration 999,999,999,999 is uniform.
def getUniformIntegerCountInInterval(A: int, B: int) -> int: '''Iteration is too slow - time limit exceeded''' res = 0 for i in range(A, B+1): i_str = str(i) if len(set(i_str)) == 1: res += 1 return res
def getUniformIntegerCountInInterval(A: int, B: int) -> int: ''' Intuition: each order has only 9 uniform numbers (1-9, 10-99, 100-999, etc.) which is not a lot Get all of uniform numbers from order 1 to the order of B. E.g. from 1 to 999 if B is 300 or order 3 (digit order)), and then apply cutoffs A and B and return the length of the remaining list ''' digits = [1, 2, 3, 4, 5, 6, 7, 8, 9] max_ = len(str(B)) res = [] for i in range(1, max_+1): coef = int(''.join(['1']*i)) res.extend([coef*j for j in digits]) return len([ i for i in res if i >= A and i <= B ])
# Expected Return Value = 5 A = 75 B = 300 print( getUniformIntegerCountInInterval(A, B) ) # Expected Return Value = 9 A = 1 B = 9 print( getUniformIntegerCountInInterval(A, B) ) # Expected Return Value = 1 A = 999999999999 B = 999999999999 print( getUniformIntegerCountInInterval(A, B) )
5 9 1