Coding problems and hints I (30 problems)

अब Quizwiz के साथ अपने होमवर्क और परीक्षाओं को एस करें!

Leetcode Study Plan - Algorithm I - Day 1 - Binary Search 35. Search Insert Position Easy Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [1,3,5,6], target = 5 Output: 2 Example 2: Input: nums = [1,3,5,6], target = 2 Output: 1 Example 3: Input: nums = [1,3,5,6], target = 7 Output: 4 Constraints: 1 <= nums.length <= 104 -104 <= nums[i] <= 104 nums contains distinct values sorted in ascending order. -104 <= target <= 104 Accepted 1,244,843 Submissions 2,928,302

Hints: - Binary Search - In case of the target is not found, instead of returning -1, return left as that is the position the target will be inserted if not found. Accepted code: O(Log N) class Solution: def searchInsert(self, nums: List[int], target: int) -> int: left, right = 0, len(nums) - 1 while left <= right: pivot = left + (right - left) // 2 if target == nums[pivot]: return pivot elif target < nums[pivot]: right = pivot -1 else: left = pivot + 1 return left

Leetcode Study Plan - Dynamic Programming I - Day 1 509. Fibonacci Number Easy The Fibonacci numbers, commonly denoted F(n) form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. That is, F(0) = 0, F(1) = 1 F(n) = F(n - 1) + F(n - 2), for n > 1. Given n, calculate F(n). Example 1: Input: n = 2 Output: 1 Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1. Example 2: Input: n = 3 Output: 2 Explanation: F(3) = F(2) + F(1) = 1 + 1 = 2. Example 3: Input: n = 4 Output: 3 Explanation: F(4) = F(3) + F(2) = 2 + 1 = 3. Constraints: 0 <= n <= 30

Hints: - Dynamic Programming - Recursion - Math Accepted code: O(N) DP - Memoization approach with recursion class Solution: fibval = {0:0, 1:1} def fib(self, n: int) -> int: if n in self.fibval: return self.fibval[n] self.fibval[n] = self.fib(n-1) + self.fib(n-2) return self.fibval[n] DP - Memoization approach with iteration class Solution: fibval = {0:0, 1:1} def fib(self, n: int) -> int: if n <= 1: return n for i in range(2,n+1): self.fibval[i] = self.fibval[i-1]+self.fibval[i-2] return self.fibval[n] Alternate approaches: Approach: Matrix Exponentiation Intuition Use Matrix Exponentiation to get the Fibonacci number from the element at (0, 0) in the resultant matrix. In order to do this we can rely on the matrix equation for the Fibonacci sequence, to find the Nth Fibonacci number: [[1,1],[1,0]]^n = [[n+1, n], [n, n-1]] Algorithm Check if N is less than or equal to 1. If it is, return N. Use a recursive function, matrixPower, to calculate the power of a given matrix A. The power will be N-1, where N is the Nth Fibonacci number. The matrixPower function will be performed for N/2 of the Fibonacci numbers. Within matrixPower, call the multiply function to multiply 2 matrices. Once we finish doing the calculations, return A[0][0] to get the Nth Fibonacci number. Implementation class Solution: def fib(self, N: int) -> int: if (N <= 1): return N A = [[1, 1], [1, 0]] self.matrix_power(A, N - 1) return A[0][0] def matrix_power(self, A: List[List[int]], N: int): if (N <= 1): return A self.matrix_power(A, N // 2) self.multiply(A, A) B = [[1, 1], [1, 0]] if (N % 2 != 0): self.multiply(A, B) def multiply(self, A: List[List[int]], B: List[List[int]]) -> None: x = A[0][0] * B[0][0] + A[0][1] * B[1][0] y = A[0][0] * B[0][1] + A[0][1] * B[1][1] z = A[1][0] * B[0][0] + A[1][1] * B[1][0] w = A[1][0] * B[0][1] + A[1][1] * B[1][1] A[0][0] = x A[0][1] = y A[1][0] = z A[1][1] = w Complexity Analysis Time complexity: O(logN). By halving the N value in every matrixPower's call to itself, we are halving the work needed to be done. Space complexity: O(logN). The size of the stack in memory is proportional to the function calls to matrixPower plus the memory used to account for the matrices which use constant space. Approach: Math Intuition Let's use the golden ratio, a.k.a Binet's formula: (1+sqrt(5)/2 = approx 1.6180339887.... We can derive the most efficient solution to this problem using only constant space! Algorithm Use the golden ratio formula to calculate the Nth Fibonacci number. Implementation class Solution: def fib(self, N: int) -> int: golden_ratio = (1 + (5 ** 0.5)) / 2 return int(round((golden_ratio ** N) / (5 ** 0.5))) Complexity Analysis Time complexity: O(logN). We do not use loops or recursion, so the time required equals the time spent performing the calculation using Binet's formula. However, raising the golden_ratio to the power of N requires O(logN) time. Space complexity: O(1)O(1). The space used is the space needed to create the variable to store the golden ratio.

Leetcode Study Plan - Dynamic Programming I - Day 2 70. Climbing Stairs Easy You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? Example 1: Input: n = 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps Example 2: Input: n = 3 Output: 3 Explanation: There are three ways to climb to the top. 1. 1 step + 1 step + 1 step 2. 1 step + 2 steps 3. 2 steps + 1 step Constraints: 1 <= n <= 45 Accepted 1,389,352 Submissions 2,742,291

Hints: - Graph & Tree - Since there are two options for every step, Binary Tree - Dynamic Programming - Recursion with Memoization - Each step has two choices i.e. taking 1 step or taking two steps - Consider this as a binary tree - In this tree, we go through all the paths and select only the paths that end up with steps equal to n. Accepted code: O(n) - Recursion with memoization class Solution: def climbStairs(self, n: int) -> int: steps = {} def cs(i): if i > n: return 0 if i == n: return 1 if i in steps: return steps[i] steps[i] = cs(i+1) + cs(i+2) return steps[i] return cs(0) Accepted code: O(n) - Dynamic Programming Algorithm As we can see this problem can be broken into subproblems, and it contains the optimal substructure property i.e. its optimal solution can be constructed efficiently from optimal solutions of its subproblems, we can use dynamic programming to solve this problem. One can reach I th step in one of the two ways: Taking a single step from (i-1) th step or taking a step of 2 from (i-2) th step. So, the total number of ways to reach I the step is equal to the sum of ways of reaching (i-1) th step and ways of reaching the (i-2) th step. Let dp[i] denotes the number of ways to reach on i th step: dp[i] = dp[i-1] + dp[i-2] class Solution: def climbStairs(self, n: int) -> int: if n == 1: return 1 dp = [0]*(n+1) dp[1], dp[2] = 1, 2 for i in range(3, n+1): dp[i] = dp[i-1] + dp[i-2] return dp[n]

Leetcode Study Plan - Data Structure - Day 1 - Array 217. Contains Duplicate Easy Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct. Example 1: Input: nums = [1,2,3,1] Output: true Example 2: Input: nums = [1,2,3,4] Output: false Example 3: Input: nums = [1,1,1,3,3,4,3,2,4,2] Output: true Constraints: 1 <= nums.length <= 105 -109 <= nums[i] <= 109 Accepted 1,304,235 Submissions 2,167,915

Hints: - Hash Table - Simple Accepted Code: from collections import defaultdict class Solution: def containsDuplicate(self, nums: List[int]) -> bool: count = defaultdict(int) for number in nums: count[number] += 1 if count[number] > 1: return True return False

Leetcode Weekly Contest # 277 4. #2149. Rearrange Array Elements by Sign Difficulty:Medium You are given a 0-indexed integer array nums of even length consisting of an equal number of positive and negative integers. You should rearrange the elements of nums such that the modified array follows the given conditions: Every consecutive pair of integers have opposite signs. For all integers with the same sign, the order in which they were present in nums is preserved. The rearranged array begins with a positive integer. Return the modified array after rearranging the elements to satisfy the aforementioned conditions. Example 1: Input: nums = [3,1,-2,-5,2,-4] Output: [3,-2,1,-5,2,-4] Explanation: The positive integers in nums are [3,1,2]. The negative integers are [-2,-5,-4]. The only possible way to rearrange them such that they satisfy all conditions is [3,-2,1,-5,2,-4]. Other ways such as [1,-2,2,-5,3,-4], [3,1,2,-2,-5,-4], [-2,3,-5,1,-4,2] are incorrect because they do not satisfy one or more conditions. Example 2: Input: nums = [-1,1] Output: [1,-1] Explanation: 1 is the only positive integer and -1 the only negative integer in nums. So nums is rearranged to [1,-1]. Constraints: 2 <= nums.length <= 2 * 105 nums.length is even 1 <= |nums[i]| <= 105 nums consists of an equal number of positive and negative integers.

Hints: - Array - Two Pointer Accepted Code def rearrangeArray(self, nums: List[int]) -> List[int]: pos = 0 neg = 1 res = [0]*len(nums) for d in nums: if d > 0: res[pos] = d pos += 2 elif d < 0: res[neg] = d neg += 2 return res

Leetcode - Facebook Coding Questions List 567. Permutation in String Medium 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 <= 104 s1 and s2 consist of lowercase English letters.

Hints: - HashMap and Counter - Sliding Window - You can sort both strings to compare. But that is not used in this solution. - Very similar to LeetCode problem# 438. Find All Anagrams in a String. - Another technique is to compare the frequency counts of letters in both strings. This has been used in this solution. - To store the frequencies of letters, we can use HashMap or an array of size 26. - In this solution, I have used HashMap. - For the array-based solution, see the solution for the LeetCode problem# 438. Find All Anagrams in a String. Accepted code: Slow from collections import Counter def checkInclusion(self, s1: str, s2: str) -> bool: set1 = Counter(s1) l = len(s1) for i in range(len(s2)): if set1 == Counter(s2[i:i+l]): return True return False Accepted Code: 99.21% faster and 97.77% less memory from collections import Counter, defaultdict def checkInclusion(self, s1: str, s2: str) -> bool: set1 = Counter(s1) l = len(s1) set2 = Counter(s2[:l]) set2 = defaultdict(int, set2) remove = 0 for i in range(l,len(s2)): if set1 == set2: return True set2[s2[remove]] -= 1 if set2[s2[remove]] <= 0: set2.pop(s2[remove]) set2[s2[i]] += 1 remove += 1 return set1 == set2 Accepted code: O(n) - Using only one set m, n = len(s1), len(s2) if m > n: return False count = Counter(s1) for i in range(m): if s2[i] in count: count[s2[i]] -= 1 if min(count.values()) == 0 and max(count.values()) == 0: return True start, end = 1, m while end < n: if s2[start-1] in count: count[s2[start-1]] += 1 if s2[end] in count: count[s2[end]] -= 1 if min(count.values()) == 0 and max(count.values()) == 0: return True start += 1 end += 1 return False

Leetcode Study Plan - Dynamic Programming I - Day 2 746. Min Cost Climbing Stairs Easy You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps. You can either start from the step with index 0, or the step with index 1. Return the minimum cost to reach the top of the floor. Example 1: Input: cost = [10,15,20] Output: 15 Explanation: You will start at index 1. - Pay 15 and climb two steps to reach the top. The total cost is 15. Example 2: Input: cost = [1,100,1,1,1,100,1,1,100,1] Output: 6 Explanation: You will start at index 0. - Pay 1 and climb two steps to reach index 2. - Pay 1 and climb two steps to reach index 4. - Pay 1 and climb two steps to reach index 6. - Pay 1 and climb one step to reach index 7. - Pay 1 and climb two steps to reach index 9. - Pay 1 and climb one step to reach the top. The total cost is 6. Constraints: 2 <= cost.length <= 1000 0 <= cost[i] <= 999

Hints: - Graph and Tree - Since there are two options for every step, Binary Tree - Dynamic Programming - Recursion with Memoization - Each step has two choices i.e. taking 1 step or taking two steps - Consider this as a binary tree - In this tree, we go through all the paths and select only the paths that end up with steps equal to n. Accepted code: O(n) - Recursion with memoization class Solution: def minCostClimbingStairs(self, cost: List[int]) -> int: stepCost = {} n = len(cost)-1 def mc(step): if step >= n: return 0 if step in stepCost: return stepCost[step] stepCost1 = cost[step+1] if step + 1 <= n else 0 stepCost2 = cost[step+2] if step + 2 <= n else 0 stepCost1 = stepCost1 + mc(step+1) stepCost2 = stepCost2 + mc(step+2) stepCost[step] = min(stepCost1, stepCost2) return stepCost[step] return mc(-1) Approach: Bottom-Up, Constant Space Intuition You may have noticed that our recurrence relation from the previous two approaches only cares about 2 steps below the current step. For example, if we are calculating the minimum cost to reach step 12, we only care about data from step 10 and step 11. While we would have needed to calculate the minimum cost for steps 2-9 as well, at the time of the actual calculation for step 12, we no longer care about any of those steps. Therefore, instead of using O(n)O(n) space to keep an array, we can improve to O(1)O(1) space using only two variables. Algorithm Initialize two variables, downOne and downTwo, that represent the minimum cost to reach one step and two steps below the current step, respectively. We will start iteration from step 2, which means these variables will initially represent the minimum cost to reach steps 0 and 1, so we will initialize each of them to 0. Iterate over the array, again with 1 extra iteration at the end to treat the top floor as the final "step". At each iteration, simulate moving 1 step up. This means downOne will now refer to the current step, so apply our recurrence relation to update downOne. downTwo will be whatever downOne was prior to the update, so let's use a temporary variable to help with the update. In the end, since we treated the top floor as a step, downOne will refer to the minimum cost to reach the top floor. Return downOne. Implementation Complexity Analysis Given NN as the length of cost, Time complexity: O(N)O(N). We only iterate N - 1 times, and at each iteration we apply an equation that uses O(1)O(1) time. Space complexity: O(1)O(1) The only extra space we use is 2 variables, which are independent of input size. Accepted code: class Solution: def minCostClimbingStairs(self, cost: List[int]) -> int: stepCost1 = stepCost2 = 0 for i in range(2, len(cost)+1): temp = stepCost1 stepCost1 = min(stepCost1 + cost[i-1], stepCost2 + cost[i-2]) stepCost2 = temp return stepCost1

Leetcode - Facebook Coding Questions List 173. Binary Search Tree Iterator Medium 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?

Hints: Solution Approach 1: Flatten BST Before looking at the solutions for this problem, let's try and boil down what the problem statement essentially asks us to do. So, we need to implement an iterator class with two functions namely next() and hasNext(). The hasNext() function returns a boolean value indicating whether there are any more elements left in the binary search tree or not. The next() function returns the next smallest element in the BST. Therefore, the first time we call the next() function, it should return the smallest element in the BST and likewise, when we call next() for the very last time, it should return the largest element in the BST. You might be wondering as to what could be the use case for an iterator. Essentially, an iterator can be used to iterate over any container object. For our purpose, the container object is a binary search tree. If such an iterator is defined, then the traversal logic can be abstracted out and we can simply make use of the iterator to process the elements in a certain order. 1. new_iterator = BSTIterator(root); 2. while (new_iterator.hasNext()) 3. process(new_iterator.next()); Usually, an iterator simply goes over each of the elements of the container one by one. For the BST, we want the iterator to return elements in ascending order. An important property of the binary search tree is that the inorder traversal of a BST gives us the elements in sorted order. Thus, the inorder traversal will be the core of the solutions that we will look ahead. Naturally, the easiest way to implement an iterator would be on an array-like container interface. So, if we had an array, all we would need is a pointer or an index and we could easily implement the two required functions next() and hasNext(). Array-based approach: Flatten the BST We will be using additional memory and we will flatten the binary search tree into an array. Since we need the elements to be in sorted order, we will do an inorder traversal over the tree and store the elements in a new array and then build the iterator functions using this new array. Algorithm Initialize an empty array that will contain the nodes of the binary search tree in 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 the next function, we simply return the element pointed by the index. Also, after the next function call is made, we have to move the index one step forward to simulate the progress of our iterator. Accepted Code: Flatterning and Recursion # 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 BSTIterator: def __init__(self, root: Optional[TreeNode]): self.sorted_nodes = [] self.pointer = -1 self.flatten(root) self.size = len(self.sorted_nodes) def flatten(self, node): if node is None: return self.flatten(node.left) self.sorted_nodes.append(node.val) self.flatten(node.right) def next(self) -> int: if self.pointer < len(self.sorted_nodes)-1: self.pointer += 1 return self.sorted_nodes[self.pointer] def hasNext(self) -> bool: return self.pointer < len(self.sorted_nodes)-1 # Your BSTIterator object will be instantiated and called as such: # obj = BSTIterator(root) # param_1 = obj.next() # param_2 = obj.hasNext() Approach 2: Controlled Recursion # 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 BSTIterator: def __init__(self, root: Optional[TreeNode]): self.stack = [] self.leftmost(root) def leftmost(self, root): if root is None: return while root: self.stack.append(root) root = root.left def next(self) -> int: node = self.stack.pop() if node.right: self.leftmost(node.right) return node.val def hasNext(self) -> bool: return len(self.stack) > 0 # Your BSTIterator object will be instantiated and called as such: # obj = BSTIterator(root) # param_1 = obj.next() # param_2 = obj.hasNext()

Leetcode Study Plan - Data Structure - Day 1 - Array 53. Maximum Subarray Easy Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum. A subarray is a contiguous part of an array. Example 1: Input: nums = [-2,1,-3,4,-1,2,1,-5,4] Output: 6 Explanation: [4,-1,2,1] has the largest sum = 6. Example 2: Input: nums = [1] Output: 1 Example 3: Input: nums = [5,4,-1,7,8] Output: 23 Constraints: 1 <= nums.length <= 105 -104 <= nums[i] <= 104 Follow up: If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

Hints: - Array and Two pointers an DP - Two pointers: maxSum and curSum - maxSum holds sum of any maxSubarray (set of contiguous elements in the array) at any point of time. - curSum holds sum of current subarray (set of contiguous elements in the array) at any point of time. - As we progress and iterate, update the maxSum if curSum is greater than maxSum. - Always, curSum <= maxSum. - If any element is greater than curSum, then update curSum with that element. Accepted Code: O(N) - Two Pointers or DP class Solution: def maxSubArray(self, nums: List[int]) -> int: maxSum = curSum = nums[0] for num in nums[1:]: curSum = max(num, curSum + num) maxSum = max(curSum, maxSum) return maxSum Divide and Conquer Approach This approach is slower and less intuitive than the second approach and uses more space, but it's still a nice and different way to approach the problem. Divide and conquer algorithms involve splitting up the input into smaller chunks until they're small enough to be easily solved, and then combining the solutions to get the final overall solution. Intuition: This is intuition only and it goes from smallest chunk of the problem to biggest. When we apply this intuition, we will go from biggest chunk to smallest. - If we split the array in to single element subarrays and get maximum, we have localized solutions for all subarrays as the element itself. - Now, move on to increasing the chunk size. Next step is to have subarrays of two elements. It is still easy to get localized solutions for each chunck, as the maxsubarray is either left element or right element or left+right, whichever is biggest value. - Let us consider the subarrays with three elements. The localized solution for each subarray of three elements is either the left element or right element or middle element or left + middle element or right + element or left+middle+right element. - As we go further, we end up with a subarray of size of original array. The solution for the original array is maximum sum of all localized solutions that we found. - We will implement this intution as algorithm, but going from big to small subarray. Complexity Analysis Time complexity: O(N \cdot \log N)O(N⋅logN), where NN is the length of nums. On our first call to findBestSubarray, we use for loops to visit every element of nums. Then, we split the array in half and call findBestSubarray with each half. Both those calls will then iterate through every element in that half, which combined is every element of nums again. Then, both those halves will be split in half, and 4 more calls to findBestSubarray will happen, each with a quarter of nums. As you can see, every time the array is split, we still need to handle every element of the original input nums. We have to do this \log NlogN times since that's how many times an array can be split in half. Space complexity: O(\log N)O(logN), where NN is the length of nums. The extra space we use relative to input size is solely occupied by the recursion stack. Each time the array gets split in half, another call of findBestSubarray will be added to the recursion stack, until calls start to get resolved by the base case - remember, the base case happens at an empty array, which occurs after \log NlogN calls. Accepted Code: Divide and Conquer - O(N Log class Solution: def maxSubArray(self, nums: List[int]) -> int: # O(N) solution with DP # maxSum = curSum = nums[0] # for num in nums[1:]: # curSum = max(num, curSum + num) # maxSum = max(curSum, maxSum) # return maxSum # divide and conquer def dcMaxSubarray(nums, left, right): if left > right: return -math.inf mid = (left + right) // 2 currSum = maxLeftSum = maxRightSum = 0 for i in range(mid-1, left-1, -1): currSum += nums[i] maxLeftSum = max(maxLeftSum, currSum) currSum = 0 for i in range(mid+1, right+1): currSum += nums[i] maxRightSum = max(maxRightSum, currSum) maxSum = maxLeftSum + nums[mid] + maxRightSum leftSum = dcMaxSubarray(nums, left, mid-1) rightSum = dcMaxSubarray(nums, mid+1, right) return max(maxSum, leftSum, rightSum) return dcMaxSubarray(nums, 0, len(nums) - 1)

Leetcode Study Plan - Algorithm I - Day 1 - Binary Search 278. First Bad Version Easy 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

Hints: - Binary Search - Since we need to find a pivot point where either pivot is good version and pivot+1 is bad version or pivot-1 is good version and pivot is bad version, we can use binary search to find this pivot. - We start with pivot as left + (right - left) // 2 (i.e. middle of 1 to n). - We skip all the numbers on the right side of the pivot on each iteration if pivot is good version. - We skip all the numbers on the left size of the pivot on each iteration if pivot is bad version. - If our final pivot is bad version, return pivot else if our final pivot is good version, then return pivot + 1 Accepted code: O(Log N) # The isBadVersion API is already defined for you. # def isBadVersion(version: int) -> bool: class Solution: def firstBadVersion(self, n: int) -> int: left, right = 1, n while left <= right: pivot = left + (right - left) // 2 if isBadVersion(pivot): right = pivot - 1 else: if left == right: return pivot + 1 left = pivot + 1 return pivot

Leetcode Study Plan - Algorithm I - Day 1 - Binary Search 704. Binary Search Easy Given an array of integers nums which is sorted in ascending order, and an integer target, write a function to search target in nums. If target exists, then return its index. Otherwise, return -1. You must write an algorithm with O(log n) runtime complexity. Example 1: Input: nums = [-1,0,3,5,9,12], target = 9 Output: 4 Explanation: 9 exists in nums and its index is 4 Example 2: Input: nums = [-1,0,3,5,9,12], target = 2 Output: -1 Explanation: 2 does not exist in nums so return -1 Constraints: 1 <= nums.length <= 104 -104 < nums[i], target < 104 All the integers in nums are unique. nums is sorted in ascending order.

Hints: - Binary Search - Take advantage of ascending ordered array - Keep diving the array by left, pivot and right - If target == pivot, then return pivot - If target is less than pivot, then move left - If target is greater than pivot, then move right. Accepted code: O(log N) class Solution: def search(self, nums: List[int], target: int) -> int: left, right = 0, len(nums) - 1 while left <= right: pivot = left + (right - left) // 2 if target == nums[pivot]: return pivot if target < nums[pivot]: right = pivot - 1 else: left = pivot + 1 return -1

Leetcode Weekly Contest # 277 5. #2150. Find All Lonely Numbers in the Array You are given an integer array nums. A number x is lonely when it appears only once, and no adjacent numbers (i.e. x + 1 and x - 1) appear in the array. Return all lonely numbers in nums. You may return the answer in any order. Example 1: Input: nums = [10,6,5,8] Output: [10,8] Explanation: - 10 is a lonely number since it appears exactly once and 9 and 11 does not appear in nums. - 8 is a lonely number since it appears exactly once and 7 and 9 does not appear in nums. - 5 is not a lonely number since 6 appears in nums and vice versa. Hence, the lonely numbers in nums are [10, 8]. Note that [8, 10] may also be returned. Example 2: Input: nums = [1,3,5,3] Output: [1,5] Explanation: - 1 is a lonely number since it appears exactly once and 0 and 2 does not appear in nums. - 5 is a lonely number since it appears exactly once and 4 and 6 does not appear in nums. - 3 is not a lonely number since it appears twice. Hence, the lonely numbers in nums are [1, 5]. Note that [5, 1] may also be returned. Constraints: 1 <= nums.length <= 105 0 <= nums[i] <= 106

Hints: - Counter - Hash Accepted Code: Simple Solution - TC: O(N) but SC: O(N) def findLonely(self, nums: List[int]) -> List[int]: adjacents = Counter(nums) result = [] for d in nums: if d+1 not in adjacents and d-1 not in adjacents and adjacents[d] == 1: result.append(d) return result

Leetcode Study Plan - Dynamic Programming I - Day 2 198. House Robber Medium You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night. Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police. Example 1: Input: nums = [1,2,3,1] Output: 4 Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). Total amount you can rob = 1 + 3 = 4. Example 2: Input: nums = [2,7,9,3,1] Output: 12 Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1). Total amount you can rob = 2 + 9 + 1 = 12. Constraints: 1 <= nums.length <= 100 0 <= nums[i] <= 400

Hints: - Dynamic Programming - Break the problem into smaller sub-problems - Can be solved as recursion with memorization or pure Dynamic Programming - When the robber is at ith house, he should take a decision that the money he got till i-1 house is greater than the money he got till i-2 plus the money in the current house. - MoneyTillThisHouse(i+2) = max(MoneyTillThisHouse(i+1), MoneyTillThisHouse(i) + MoneyInThishouse(i) - Initialize MoneyTillThisHouse(i) as 0 as Robber has not accumulated any money when he arrives at ith house and MoneyTillThisHouse(i+1) as MoneyInThishouse(i+1) Accepted code: O(n) - Pure DP class Solution: def rob(self, nums: List[int]) -> int: n = len(nums) moneyTillThisHouse = [0]*(n+1) moneyTillThisHouse[0] = 0 moneyTillThisHouse[1] = nums[0] for i in range(2,n+1): moneyTillThisHouse[i] = max(moneyTillThisHouse[i-1], moneyTillThisHouse[i-2]+nums[i-1]) return moneyTillThisHouse[n]

Leetcode Study Plan - Dynamic Programming I - Day 1 1137. N-th Tribonacci Number Easy The Tribonacci sequence Tn is defined as follows: T0 = 0, T1 = 1, T2 = 1, and Tn+3 = Tn + Tn+1 + Tn+2 for n >= 0. Given n, return the value of Tn. Example 1: Input: n = 4 Output: 4 Explanation: T_3 = 0 + 1 + 1 = 2 T_4 = 1 + 1 + 2 = 4 Example 2: Input: n = 25 Output: 1389537 Constraints: 0 <= n <= 37 The answer is guaranteed to fit within a 32-bit integer, ie. answer <= 2^31 - 1.

Hints: - Dynamic Programming - Recursion - Math Accepted code: O(N) DP with recursion and memoization class Solution: fibval = {0:0, 1:1, 2:1} def tribonacci(self, n: int) -> int: if n in self.fibval: return self.fibval[n] self.fibval[n] = self.tribonacci(n-3) + self.tribonacci(n-2) + self.tribonacci(n-1) return self.fibval[n] Accepted code: O(N) DP with iteration and memoization class Solution: fibval = {0:0, 1:1, 2:1} def tribonacci(self, n: int) -> int: if n in self.fibval: return self.fibval[n] for i in range(3,n+1): self.fibval[i] = self.fibval[i-3] + self.fibval[i-2] + self.fibval[i-1] return self.fibval[n]

Leetcode Study Plan - Data Structure - Day 2 - Array 1. Two Sum Easy Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have exactly one solution, and you may not use the same element twice. You can return the answer in any order. Example 1: Input: nums = [2,7,11,15], target = 9 Output: [0,1] Explanation: Because nums[0] + nums[1] == 9, we return [0, 1]. Example 2: Input: nums = [3,2,4], target = 6 Output: [1,2] Example 3: Input: nums = [3,3], target = 6 Output: [0,1] Constraints: 2 <= nums.length <= 104 -109 <= nums[i] <= 109 -109 <= target <= 109 Only one valid answer exists. Follow-up: Can you come up with an algorithm that is less than O(n2) time complexity?

Hints: - Hash Table - Complement numbers Accepted code: O(N) class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: comps = defaultdict(int) for i, num in enumerate(nums): if num in comps: return i, comps[num] comps[target-num] = i

Leetcode Study Plan - Data Structure - Day 3 - Array 350. Intersection of Two Arrays II Easy Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear as many times as it shows in both arrays and you may return the result in any order. 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. Constraints: 1 <= nums1.length, nums2.length <= 1000 0 <= nums1[i], nums2[i] <= 1000 Follow up: What if the given array is already sorted? How would you optimize your algorithm? What if nums1's size is small compared to nums2's size? Which algorithm is better? What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once?

Hints: - Hash Table - Counter Accepted code: O(m+n) class Solution: def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: # use smaller array to convert to hashmap if len(nums1) > len(nums2): return intersect(nums2, nums1) count = Counter(nums1) output = [] for num in nums2: if num in count: count[num] -= 1 output.append(num) if count[num] == 0: del count[num] return output Answers to follow-up questions: Follow up: What if the given array is already sorted? How would you optimize your algorithm? - Use two pointer method - TC: O(max(m,n)) - Code: (Not submitted) def intersect(nums1, nums2): output=[] i, j = len(nums1)-1, len(nums2)-1 while i >= 0 and j >= 0: if nums1[i] == nums2[j]: output.append(nums1) i -= 1 j -= 1 elif nums1[i] < nums2[j]: j -= 1 elif nums1[i] > nums2[j]: i -= 1 return output What if nums1's size is small compared to nums2's size? Which algorithm is better? - We can use first one with Hash Map as we convert smaller one. What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once? - If nums1 fits into the memory, we can use Approach 1 to collect counts for nums1 into a hash map. Then, we can sequentially load and process nums2. If neither of the arrays fit into the memory, we can apply some partial processing strategies: Split the numeric range into subranges that fits into the memory. Modify Approach 1 to collect counts only within a given subrange, and call the method multiple times (for each subrange). Use an external sort for both arrays. Modify Approach 2 to load and process arrays sequentially.

Leetcode - Facebook Coding Questions List 438. Find All Anagrams in a String Medium 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 * 104 s and p consist of lowercase English letters.

Hints: - HashMap, Counter, or array of 26. - Sliding Window. - You can sort both strings to compare. But that is not used in this solution. - Very similar to LeetCode problem# 567. Permutation in String. - Another technique is to compare the frequency counts of letters in both strings. This has been used in this solution. - To store the frequencies of letters, we can use HashMap or an array of size 26. - In this solution, I have used an array of size 26. - For the Hashmap-based solution, see the solution for the LeetCode problem# 567. Permutation in String. Accepted Code: def findAnagrams(self, s: str, p: str) -> List[int]: l, m = len(p), len(s) if l > m: return [] set1, set2 = [0]*26, [0]*26 result = [] for i in range(l): set1[ord(p[i])-97] += 1 for i in range(l): set2[ord(s[i])-97] += 1 for i in range(l, m): if set1 == set2: result.append(i-l) set2[ord(s[i-l])-97] -= 1 set2[ord(s[i])-97] += 1 if set1 == set2: result.append(m-l) return result

Leetcode - Facebook Coding Questions List 953. Verifying an Alien Dictionary Easy 2503871Add to ListShare 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.

Hints: - HashTable - Strings - Compare order values of neighboring words by looking up HashTable. - Edge cases are: both words are same, letters in the second word is subset if first word. Accepted Code - O(N) from collections import defaultdict def isAlienSorted(self, words: List[str], order: str) -> bool: n = len(words) alpha = defaultdict(int) i = 1 for c in order: alpha[c] = i i += 1 for i in range(n-1): word1 = words[i] word2 = words[i+1] l, m = len(word1), len(word2) j = 0 while j < min(l,m): if alpha[word1[j]] < alpha[word2[j]]: break elif alpha[word1[j]] == alpha[word2[j]]: j += 1 else: return False if l > m and j == m: return False return True

Leetcode - Facebook Coding Questions List 297. Serialize and Deserialize Binary Tree Hard 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

Hints: - I have chosen to use nested dictionary to serialize the binary tree so that it can be converted to a json string which is industry standard format as well as it is a string. This is universally compatible and portable. - Use recursion to serialize the binary tree to nested dictionary. - Use json module to convert it to json. - Use json module to convert json string back to dictionary. - Use BFS non-recursive (queue) to rebuild binary tree from nested dictionary. - This approach may be slower than using plain string, however it has the benefit of greater portability and compatibility. Accepted code: # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None import json from collections import defaultdict class Codec: def serialize(self, root): # Encodes a tree to a single string. # :type root: TreeNode # :rtype: str def pickle(node, serial, nodetype): if node is None: return serial.setdefault("None", {}) serial = serial.setdefault(nodetype, {}) serial = serial.setdefault(node.val, {}) pickle(node.left, serial, "left") pickle(node.right, serial, "right") serialroot = dict() serial = serialroot pickle(root, serial, "root") serialroot = json.dumps(serialroot) return serialroot def deserialize(self, data): # Decodes your encoded data to tree. # :type data: str # :rtype: TreeNode data = json.loads(data) root = TreeNode() queue = [] if 'None' in data.keys(): return data = data['root'] queue.append((root, data)) while queue: d = queue.pop(0) node = d[0] data = d[1] for key, val in data.items(): node.val = key if 'left' in data[node.val]: node.left = TreeNode() queue.append((node.left, data[node.val]['left'])) if 'right' in data[node.val]: node.right = TreeNode() queue.append((node.right, data[node.val]['right'])) # utility to print all the nodes in binary tree # using BFS and queue. # def printNodes(root): # queue = [] # if root is None: # return # queue.append(root) # while queue: # node = queue.pop(0) # print(node.val) # if node.left is not None: # queue.append(node.left) # if node.right is not None: # queue.append(node.right) # # printNodes(root) return root # Your Codec object will be instantiated and called as such: # ser = Codec() # deser = Codec() # ans = deser.deserialize(ser.serialize(root)) # sample test cases: # input: root = [1,2,3,null,null,4,5] # output: [1,2,3,null,null,4,5] # input: root = [] # output: []

Leetcode - Facebook Coding Questions List 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 <= starti < endi <= 109 endi < starti+1 0 <= startj < endj <= 109 endj < startj+1

Hints: - No special data structures required except the array. - Typical interval trick. - Since the input is already sorted, the solution become simple. - For any intervals that we compare from both list, we need to consider minimum of starts and maximum of ends of intervals. - If min(starti, startj) <= max(endi, endj) then add [minStart, maxEnd] to output list. - At each iteration, check if endi < endj and move i (interval from firstlist) by 1 else move j (interval from secondlist) by 1. Accepted Code: O(N) def intervalIntersection(self, firstList: List[List[int]], secondList: List[List[int]]) -> List[List[int]]: overlappedInt = [] i, j = 0, 0 n = len(firstList) m = len(secondList) while i < n and j < m: start = max(firstList[i][0], secondList[j][0]) end = min(firstList[i][1], secondList[j][1]) if start <= end: overlappedInt.append([start, end]) if firstList[i][1] < secondList[j][1]: i += 1 elif firstList[i][1] > secondList[j][1]: j += 1 else: i += 1 j += 1 return overlappedInt

Leetcode - Facebook Coding Questions List 282. Expression Add Operators Hard (not fully finished) 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: ["1*2*3","1+2+3"] Explanation: Both "1*2*3" and "1+2+3" evaluate to 6. Example 2: Input: num = "232", target = 8 Output: ["2*3+2","2+3*2"] Explanation: Both "2*3+2" and "2+3*2" 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. -231 <= target <= 231 - 1

Hints: - Since the question asks for all possibilities, we need to consider all expressions i.e. 4 ^ N (4 because four operators i.e. "", "+", "-", "*". "" indicates no-op.) - Recursion and backtracking to get all the possibilities - While evaluating the expression, note that an operand can contain multiple digits due to the "no-op" option. - It is efficient to evaluate each term as the terms are added to expression to avoid separate evaluation - The multiplication operator will have precedence so, sometimes we may have to go back and reverse the prior ops to do multiply ops. def addOperators(self, num: str, target: int) -> List[str]: def evaluate(prevOp, prevOpnd, currOp, currOpnd, value): if currOp == '*' and prevOp == '+': value = calculate(value, '-', prevOpnd) value = calculate(value, '*', currOpnd) value = calculate(value, '+', prevOpnd) elif currOp == '*' and prevOp == '-': value = calculate(value, '+', prevOpnd) value = calculate(value, '*', currOpnd) value = calculate(value, '-', prevOpnd) elif currOp != '': value = calculate(value, currOp, currOpnd) return value def calculate(first, op, second): if op == '*': return first * second elif op == '+': return first + second elif op == '-': return first - second def allPossibilities(start, expression, prevOp, prevOpnd, currOp, value): if start == len(num) - 1: expression += num[start] currOpnd = int(num[start]) value = evaluate(prevOp, prevOpnd, currOp, currOpnd, value) if value == target: expList.append(expression) return for p in (['','*','+','-']): if start == 0: value = int(num[start]) currOpnd = int(num[start]) value = evaluate(prevOp, prevOpnd, currOp, currOpnd, value) prevOp = currOp prevOpnd = int(num[start]) currOp = p expression += num[start] expression += p prevSize = len(num[start] + p) allPossibilities(start+1,expression,prevOp, prevOpnd, currOp, value) expression = expression[:-prevSize] expList = [] allPossibilities(0,"", "", 0, "",0) return expList

Leetcode 1910. Remove All Occurrences of a Substring Medium 45830Add to ListShare Given two strings s and part, perform the following operation on s until all occurrences of the substring part are removed: Find the leftmost occurrence of the substring part and remove it from s. Return s after removing all occurrences of part. A substring is a contiguous sequence of characters in a string. Example 1: Input: s = "daabcbaabcbc", part = "abc" Output: "dab" Explanation: The following operations are done: - s = "daabcbaabcbc", remove "abc" starting at index 2, so s = "dabaabcbc". - s = "dabaabcbc", remove "abc" starting at index 4, so s = "dababc". - s = "dababc", remove "abc" starting at index 3, so s = "dab". Now s has no occurrences of "abc". Example 2: Input: s = "axxxxyyyyb", part = "xy" Output: "ab" Explanation: The following operations are done: - s = "axxxxyyyyb", remove "xy" starting at index 4 so s = "axxxyyyb". - s = "axxxyyyb", remove "xy" starting at index 3 so s = "axxyyb". - s = "axxyyb", remove "xy" starting at index 2 so s = "axyb". - s = "axyb", remove "xy" starting at index 1 so s = "ab". Now s has no occurrences of "xy". Constraints: 1 <= s.length <= 1000 1 <= part.length <= 1000 s​​​​​​ and part consists of lowercase English letters.

Hints: - Sliding Window - String Accepted code: With built-in method def removeOccurrences(self, s: str, part: str) -> str: while part in s: string = s.replace(part, '',1) s = string return s Without built-in method def removeOccurrences(self, s: str, part: str) -> str: part = list(part) ws = len(part) stack = [] for i in range(len(s)): stack.append(s[i]) if stack[-ws::] == part: stack = stack[:-ws] return "".join(stack)

Leetcode Study Plan - Data Structure - Day 2 - Array 88. Merge Sorted Array Easy You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, representing the number of elements in nums1 and nums2 respectively. Merge nums1 and nums2 into a single array sorted in non-decreasing order. The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, and the last n elements are set to 0 and should 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?

Hints: - Two pointer - One pointer to track index i of nums1 and another pointer to track index j of nums2 - At any index i or j, either nums1[i] < nums2[j] or nums1[i] >= nums2[j]. - For first case, add nums1[i] to temporary array and advance i by 1. - For second case, add nums2[j] to temporary array and advance j by 1. - At end of iteration, add all remaining numbers from either nums1 or nums2 to temporary. - Copy all the elements from temporary to nums1. Accepted code: O(m+n) class Solution: def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: """ Do not return anything, modify nums1 in-place instead. """ if n > 0 and m == 0: for i in range(n): nums1[i] = nums2[i] elif n > 0 and m > 0: temp = []*(m+n) i = j = 0 while i < m and j < n: if nums1[i] < nums2[j]: temp.append(nums1[i]) i += 1 else: temp.append(nums2[j]) j += 1 if i < m: temp += nums1[i:] elif j < n: temp += nums2[j:] for i in range(m+n): nums1[i] = temp[i]

Leetcode Study Plan - Data Structure - Day 3 - Array 121. Best Time to Buy and Sell Stock Easy 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 <= 105 0 <= prices[i] <= 104

Hints: - Two pointers - Array - Dynamic Programming Accepted code: O(N) class Solution: def maxProfit(self, prices: List[int]) -> int: lowBuy = math.inf profit = 0 for p in prices: profit = max(profit, p-lowBuy) lowBuy = min(lowBuy, p) return profit

Leetcode - Facebook Coding Questions List 211. Add and Search Word - Data structure design Medium 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.

Hints: - Use Trie - The additional implementation other than standard Trie is search words with place holder '.' Accepted Code: class WordDictionary: def __init__(self): self.dictionary = dict() def addWord(self, word: str) -> None: curr = self.dictionary for letter in word: curr = curr.setdefault(letter, {}) curr.setdefault('__end__', '__end__') def search(self, word: str) -> bool: def searchWord(word, curr): for i, letter in enumerate(word): if letter not in curr: if letter == '.': for x in curr: if '__end__' != x and searchWord(word[i+1:], curr[x]): return True return False else: curr = curr[letter] return '__end__' in curr curr = self.dictionary return searchWord(word, curr)

Leetcode - Facebook Coding Questions List 146. LRU Cache Medium 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.

Hints: - Use hash table as cache - Keep track of get and put of keys - OrderedDict can be used as cache storage as well as deque to keep track of least recently used keys. Accepted Code: from collections import OrderedDict class LRUCache: def __init__(self, capacity: int): self.capacity = capacity self.cache = OrderedDict() def get(self, key: int) -> int: value = self.cache.get(key, -1) if value >= 0: self.cache.move_to_end(key) return value def put(self, key: int, value: int) -> None: self.cache[key] = value self.cache.move_to_end(key) if len(self.cache) > self.capacity: self.cache.popitem(False) # Your LRUCache object will be instantiated and called as such: # obj = LRUCache(capacity) # param_1 = obj.get(key) # obj.put(key,value)

Geeksforgeeks - Amazon contest - 23-Jan-2022 Sunday 8:30 AM EST 1. Max Sum without Adjacents Given an array Arr of size N containing positive integers. Find the maximum sum of a subsequence such that no two numbers in the sequence should be adjacent in the array. Example 1: Input: N = 6 Arr[] = {5, 5, 10, 100, 10, 5} Output: 110 Explanation: If you take indices 0, 3 and 5, then Arr[0]+Arr[3]+Arr[5] =5 + 100 + 5 = 110. Example 2: Input: N = 4 Arr[] = {3, 2, 7, 10} Output: 13 Explanation: 3 and 10 form a non-contiguous subsequence with maximum sum. Constraints: 1<=N<=10^6 1<=arr[i]<=10^7 Expected TC = O(N). Expected SC = O(1).

Hints: 1. Dynamic Programming. Maximization problem. 2. Doing exhaustive search cleverly by storing the results of each subproblems and reuse. 3. Subproblem and reuse - brute force - Recursion - DP - Memoization: a. Subproblem: for any i, decide which is bigger - arr[i] + are[i+2] or arr[i] + arr[i+3]. b. Initially, start thinking to solve recursively. Since each element at an index i has two options I.e. adding to element at i + 2 or i + 3, each recursive branch to arr[i] + sum(i+2) and arr[i] + sum(i+3). c. Use memoization to store the sum value that is already calculated to avoid duplicate recursion calls. 4. Subproblem and reuse - brute force - pure DP: a. In another way of thinking, at any index i, we can keep track of sum that includes the number at index i i.e. arr[i] or the sum that excludes the number at the index i. This makes sure we have two sets of sequences and their sums. At each step, we carry forward only the maximum sum of these two sequences. b. In actual implementation, start with dp[0] = 0 + arr[0] and dp[1] = max(arr[1]+0, arr[0]) i.e. maximum of sum up to this point. We have dp[-1] as maximum sum at any point of time. c. As we move forward, dp[i] would be max(arr[i] + dp[i-2], dp[i-1]) Code snippet for recursive: if i > n-1: return 0 elif i == n: return arr[i] elif i in dp.keys(): return dp[i] else: return max(arr[i] + sum(i+2) and arr[i] + sum(i+3)). Code snippet with recursive and memoization: if i > n-1: return 0 elif i == n: return arr[i] else: return max(arr[i] + sum(i+2) and arr[i] + sum(i+3)). Sample code - final DP: if n==0: return 0 elif n==1: return arr[0] elif n==2: return max(arr[0], arr[1]) else: dp=[]*n dp[0]=arr[0] dp[1]=max(arr[0], arr[1]) for i in range(2,n): dp[i]=max(arr[0]+dp[i-2], dp[i-1]) return dp[-1] Accepted code - Recursion with memoisation from collections import defaultdict def maxSumRecursion(arr,n): dp = defaultdict(int) def getSum(arr, i, dp): if i > n-1: return 0 elif i == n: return arr[i] elif i in dp.keys(): return dp[i] else: dp[i] = max(arr[i]+getSum(arr, i+2, dp), arr[i]+getSum(arr, i+3, dp)) return dp[i] for i in range(n): getSum(arr, 1, dp) return max(dp[0], dp[1]) n = 6 arr = [5, 5, 10, 100, 10, 5] n = 4 arr = [3, 2, 7, 10] print(maxSumRecursion(arr,n)) # inclusive / exclusive approach # for each i, we see if the sequence that includes the number at the index i has a bigger sum or the sequence which does not include it and has a bigger sum. We continue to store these values in DP as we move forward. Accepted code - Dynamic Programming without recursion def maxSumDP(arr,n): if n == 1: return arr[0] elif n == 2: return max(arr[0], arr[1]) else: dp = [0]*n dp[0] = arr[0] dp[1] = max(arr[0], arr[1]) for i in range(2,n): dp[i] = max(arr[i]+dp[i-2], dp[i-1]) return dp[-1] n = 6 arr = [5, 5, 10, 100, 10, 5] n = 4 arr = [3, 2, 7, 10] print(maxSumDP(arr,n))

Geeksforgeeks - Amazon contest - 23-Jan-2022 Sunday 8:30 AM EST 2. Palindrome Pairs Given an array of strings arr[] of size N, find if there exists 2 strings arr[i] and arr[j where (i != j) such that arr[i]+arr[j] is a palindrome i.e the concatenation of string arr[i] and arr[j] results into a palindrome. Example 1: Input: N = 6 arr[] = {"geekf", "geeks", "or","keeg", "abc", "bc"} Output: 1 Explanation: There is a pair "geekf" and "keeg". Example 2: Input: N = 5 arr[] = {"abc", "xyxcba", "geekst", "or", "bc"} Output: 1 Explanation: There is a pair "abc" and "xyxcba". Constraints: 1<=N<=10^4 1<=|arr[i]|<=10 Expected TC = O(N*l^2) where l = length of longest string in the array. Expected SC = O(N*l^2) where 1 = length of longest string in the array.

Hints: 1. Use TRiE Approach: Using a Trie Intuition We want to build some kind of Trie with the words. Then, we want to go down the list of words and identify all words from the Trie that our current word from the list would form a palindrome pair with. In words, we are looking for: Words in the Trie that are the reverse of our current word. Words in the Trie that start with the reverse of our current word and then finish in a palindrome. Words in the Trie that are the reverse of the first part of our current word, and then what's left of our current word forms a palindrome. Because we are interested in the reverse of words, it makes sense to put all the words into the Trie in reverse. Our word list is as follows: words = [ "A", "B", "BAN", "BANANA", "BAT", "LOLCAT", "MANA", "NAB", "NANA", "NOON", "ON", "TA", "TAC"] Case 1 with the Trie Case 1 is where a palindrome pair is formed by 2 words that are the reverse of each other. We'll use the word "BAN" as our example. The reverse of "BAN" is "NAB". Therefore, we need to use our Trie to see if the word "NAB" exists. Case 2 with the Trie Case 2 is the one where the first word is shorter than the second word. The second word starts with a palindrome, and ends with the reverse of the first word. So, how will this look in our Trie? Case 3 with the Trie Case 3 is the one where the first word is longer than the second word. In terms of our Trie, it would come up where we get to a blue node and still have some letters left from our current word. If those letters that are left form a palindrome, then we have a case 3 palindrome pair. Again, let's look at an example. This time, we are searching for the word "BANANA". Both times we reach a blue node, there is a palindrome remaining. Therefore, we find 2 pairs in this example. Algorithm We start by building the Trie. For each word, reverse it and identify its palindrome prefixes (suffixes of the reversed word). Insert the word into the Trie, and mark the final letter as an ending node, and include the word's index. Also, while inserting, note any points where the remainder of the word is a palindrome suffix by including the index in an additional list (used for case 2). Then, we go back through the list of words, looking each up in the Trie. Any of the following situations give us palindrome pairs. We have no letters left on the word and are at a word end node (case 1). We have no letters left on the word and there are indexes in the list attached to the node (case 2). We have a palindrome left on the word and are on a word end node (case 3). Complexity Analysis Let n be the number of words, and k be the length of the longest word. Time Complexity : O(k^2 . n). There were 2 major steps to the algorithm. Firstly, we needed to build the Trie. Secondly, we needed to look up each word in the Trie. Inserting each word into the Trie takes O(k) time. As well as inserting the word, we also checked at each letter whether or not the remaining part of the word was a palindrome. These checks had a cost of O(k), and with kk of them, gave a total cost of O(k^2). With nn words to insert, the total cost of building the Trie was therefore O(k^2 . n). Checking for each word in the Trie had a similar cost. Each time we encountered a node with a word ending index, we needed to check whether or not the current word we were looking up had a palindrome remaining. In the worst case, we'd have to do this kk times at a cost of kk for each time. So like before, there is a cost of k^2 for looking up a word, and an overall cost of k^2 . n for all the checks. This is the same as for the hash table approach. Space Complexity : O((k + n)^2). The Trie is the main space usage. In the worst case, each of the O(n . k) letters in the input would be on separate nodes, and each node would have up to nn indexes in its list. This gives us a worst case of O(n^2 . k), which is strictly larger than the input or the output. Inserting and looking up words only takes kk space though, because we're not generating a list of prefixes like we were in approach 2. This is insignificant compared to the size of the Trie itself. So in total, the size of the Trie has a worst case of O(k . n^2). In practice however, it'll use a lot less, as we based this on the worst case. Tries are difficult to analyze in the general case, because their performance is so dependent on the type of data going into them. As nn gets really, really, big, the Trie approach will eventually beat the hash table approach on both time and space. For the values of nn that we're dealing with in this question though, you'll probably notice that the hash table approach performs better. Accepted code - Trie def palindromePairs(self, words: List[str]) -> List[List[int]]: class Trie: def __init__(self): self.next = defaultdict(Trie) self.palindrome_suffix = [] self.wordend = -1 def makeTrie(words): root = Trie() for i, word in enumerate(words): word = word[::-1] curr = root for j, c in enumerate(word): if word[j:] == word[j:][::-1]: curr.palindrome_suffix.append(i) curr = curr.next[c] curr.wordend = i return root trie = makeTrie(words) solutions = [] for i, word in enumerate(words): curr = trie for j, c in enumerate(word): if curr.wordend != -1: if word[j:] == word[j:][::-1]: solutions.append([i, curr.wordend]) if c not in curr.next: break curr = curr.next[c] else: if curr.wordend != -1 and curr.wordend != i: solutions.append([i, curr.wordend]) for p in curr.palindrome_suffix: solutions.append([i, p]) return solutions

Leetcode Weekly Contest # 277 2151. Maximum Good People Based on Statements Hard There are two types of persons: The good person: The person who always tells the truth. The bad person: The person who might tell the truth and might lie. You are given a 0-indexed 2D integer array of statements of size n x n that represents the statements made by n people about each other. More specifically, statements[i][j] could be one of the following: 0 which represents a statement made by person i that person j is a bad person. 1 which represents a statement made by person i that person j is a good person. 2 represents that no statement is made by person i about person j. Additionally, no person ever makes a statement about themselves. Formally, we have that statements[i][i] = 2 for all 0 <= i < n. Return the maximum number of people who can be good based on the statements made by the n people. Example 1: Input: statements = [[2,1,2],[1,2,2],[2,0,2]] Output: 2 Explanation: Each person makes a single statement. - Person 0 states that person 1 is good. - Person 1 states that person 0 is good. - Person 2 states that person 1 is bad. Let's take person 2 as the key. - Assuming that person 2 is a good person: - Based on the statement made by person 2, person 1 is a bad person. - Now we know for sure that person 1 is bad and person 2 is good. - Based on the statement made by person 1, and since person 1 is bad, they could be: - telling the truth. There will be a contradiction in this case and this assumption is invalid. - lying. In this case, person 0 is also a bad person and lied in their statement. - Following that person 2 is a good person, there will be only one good person in the group. - Assuming that person 2 is a bad person: - Based on the statement made by person 2, and since person 2 is bad, they could be: - telling the truth. Following this scenario, person 0 and 1 are both bad as explained before. - Following that person 2 is bad but told the truth, there will be no good persons in the group. - lying. In this case person 1 is a good person. - Since person 1 is a good person, person 0 is also a good person. - Following that person 2 is bad and lied, there will be two good persons in the group. We can see that at most 2 persons are good in the best case, so we return 2. Note that there is more than one way to arrive at this conclusion. Example 2: Input: statements = [[2,0],[0,2]] Output: 1 Explanation: Each person makes a single statement. - Person 0 states that person 1 is bad. - Person 1 states that person 0 is bad. Let's take person 0 as the key. - Assuming that person 0 is a good person: - Based on the statement made by person 0, person 1 is a bad person and was lying. - Following that person 0 is a good person, there will be only one good person in the group. - Assuming that person 0 is a bad person: - Based on the statement made by person 0, and since person 0 is bad, they could be: - telling the truth. Following this scenario, person 0 and 1 are both bad. - Following that person 0 is bad but told the truth, there will be no good persons in the group. - lying. In this case person 1 is a good person. - Following that person 0 is bad and lied, there will be only one good person in the group. We can see that at most, one person is good in the best case, so we return 1. Note that there is more than one way to arrive at this conclusion. Constraints: n == statements.length == statements[i].length 2 <= n <= 15 statements[i][j] is either 0, 1, or 2. statements[i][i] == 2

Hints: Brute Force bit masking O(N * 2^N) Hard Accepted code: def maximumGood(self, statements: List[List[int]]) -> int: n = len(statements) ans = 0 mask = [0]*n true = [0]*n for i in range(n): for j in range(n): if statements[i][j] == 1: true[i] |= 1<<j if statements[i][j] != 2: mask[i] |= 1<<j def consistent(cur): for i in range(n): if cur & 1 << i: if (cur ^ true[i]) & mask[i]: return False return True; for i in range(1 << n): l = bin(i).count('1') if l > ans: if consistent(i): ans = l return ans

Leetcode Weekly Contest # 277 3. #2148. Count Elements With Strictly Smaller and Greater Elements Given an integer array nums, return the number of elements that have both a strictly smaller and a strictly greater element appear in nums. Example 1: Input: nums = [11,7,2,15] Output: 2 Explanation: The element 7 has the element 2 strictly smaller than it and the element 11 strictly greater than it. Element 11 has element 7 strictly smaller than it and element 15 strictly greater than it. In total there are 2 elements having both a strictly smaller and a strictly greater element appear in nums. Example 2: Input: nums = [-3,3,3,90] Output: 2 Explanation: The element 3 has the element -3 strictly smaller than it and the element 90 strictly greater than it. Since there are two elements with the value 3, in total there are 2 elements having both a strictly smaller and a strictly greater element appear in nums. Constraints: 1 <= nums.length <= 100 -105 <= nums[i] <= 105

Hints: Two points With O(N logN) with sorting: Sort the array first. Accepted Code - O(N logN) - Straight forward def countElements(self, nums: List[int]) -> int: nums.sort() result = 0 length = len(nums) start = -1 for i in range(1, length-1): if nums[i-1] < nums[i] < nums[i+1]: result += 1 elif nums[i-1] == nums[i] < nums[i+1] and start > -1: result += i - start elif nums[i-1] < nums[i] == nums[i+1]: start = i-1 return result Accepted Code - O(N) With O(N) without sorting: def countElements(self, nums: List[int]) -> int: result = 0 n = len(nums) sindex, bindex = 0, 0 smaller, bigger = [0]*n, [0]*n eligible = set() for i in range(n): if nums[sindex] < nums[i] < nums[bindex]: smaller[i] += 1 bigger[i] += 1 elif nums[sindex] < nums[i] > nums[bindex]: bigger[bindex] += 1 bindex = i smaller[i] += 1 elif nums[sindex] > nums[i] < nums[bindex]: smaller[sindex] += 1 sindex = i bigger[i] += 1 elif nums[sindex] < nums[i] == nums[bindex]: smaller[i] += 1 elif nums[sindex] == nums[i] < nums[bindex]: bigger[i] += 1 for i in range(n): if smaller[i] > 0 and bigger[i] > 0 or nums[i] in eligible: eligible.add(nums[i]) result += 1 return result Crazy Python code: from collections import Counter def countElements(self, nums: List[int]) -> int: count = Counter(nums) return 0 if len(count) < 3 else len(nums) - count[min(count)] - count[max(count)]


संबंधित स्टडी सेट्स

Using Pronouns Correctly Quiz 100%!!!!

View Set

Clinical Biochem Final 2020 Combined

View Set

Penny Chapter 14 Review Questions

View Set

Chapter 32: ASSESSMENT OF HEMATOLOGIC FXN & TREATMENT MODALITIES

View Set

Chapell/Meek (Licensure and Ordination), Chapell Meek (Licensure)

View Set