@@ -180,7 +180,28 @@ def myPow(self, x: float, n: int) -> float:
180180 res = self .myPow (x , n // 2 )
181181 return res ** 2
182182
183+ ########## 384. Shuffle an Array ##########
184+ import random
185+ class Solution :
186+ def __init__ (self , nums : List [int ]):
187+ self .original = nums
188+ self .cur = nums .copy ()
189+
190+ def reset (self ) -> List [int ]:
191+ self .cur = self .original .copy ()
192+ return self .cur
193+
194+ def shuffle (self ) -> List [int ]:
195+ """
196+ Time O(n) | O(1) Extra Space
197+ """
198+ n = len (self .cur )
199+ for i in range (n - 1 , 0 , - 1 ):
200+ random_idx = random .randint (0 , i )
201+ self .cur [i ], self .cur [random_idx ] = self .cur [random_idx ], self .cur [i ]
202+ return self .cur
183203
204+ ########## 295. Find Median from Data Stream ##########
184205import heapq
185206class MedianFinder :
186207 """
@@ -856,6 +877,90 @@ def dfs(course, mapping, visiting):
856877 return False
857878 return True
858879
880+ ############ 346. Moving Average from Data Stream ############
881+ from collections import deque
882+ class MovingAverage :
883+
884+ def __init__ (self , size : int ):
885+ self .window = deque ()
886+ self .size = size
887+ self .sum = 0
888+
889+ def next (self , val : int ) -> float :
890+ self .window .append (val )
891+ self .sum += val
892+ if len (self .window ) > self .size :
893+ self .sum -= self .window .popleft ()
894+
895+ return self .sum / len (self .window )
896+
897+ ############ 227. Basic Calculator II ############
898+ OPERANDS = set ('+-*/' )
899+ class Solution :
900+ def calculate (self , s : str ) -> int :
901+ """
902+ We use a stack to keep track of numbers and intermediate results.
903+ We iterate through the string, building up numbers digit by digit.
904+ When we encounter an operator or reach the end of the string, we perform the operation based on the previous sign:
905+
906+ For '+', we push the number onto the stack.
907+ For '-', we push the negative of the number.
908+ For '*', we pop the last number, multiply it by the current number, and push the result.
909+ For '/', we pop the last number, divide it by the current number (using integer division), and push the result.
910+
911+
912+ After processing all characters, we sum up all numbers in the stack to get the final result.
913+ """
914+ stack = [] # only stores intermediary value result, in the end res == sum(stack)
915+ num = 0
916+ lastsign = '+'
917+
918+ for i , char in enumerate (s ):
919+ if char .isdigit ():
920+ num = num * 10 + int (char )
921+
922+ if char in OPERANDS or i == len (s )- 1 :
923+ # when a new sign is encountered, we flush the current number
924+ # and immediately compute last expression consisting of stack[-1] OPERAND curnum
925+ # and reset curnum to zero, and update last sign
926+ if lastsign == '+' :
927+ stack .append (num )
928+ elif lastsign == '-' :
929+ stack .append (- num )
930+ elif lastsign == '*' :
931+ stack .append (stack .pop () * num )
932+ elif lastsign == '/' :
933+ stack .append (int (stack .pop () / num )) # caveat: don't use //, because // rounds UP when result is negative
934+
935+ lastsign = char
936+ num = 0
937+
938+ return sum (stack )
939+
940+
941+ ############ 16. 3Sum Closest ############
942+ class Solution :
943+ def threeSumClosest (self , nums : List [int ], target : int ) -> int :
944+ res = float ('inf' )
945+ diff = float ('inf' )
946+ nums .sort ()
947+ n = len (nums )
948+ for i in range (n - 2 ):
949+ j , k = i + 1 , n - 1
950+ while j < k :
951+ s = nums [i ] + nums [j ] + nums [k ]
952+ d = s - target
953+ if abs (d ) < diff :
954+ res = s
955+ diff = abs (d )
956+ if d > 0 :
957+ k -= 1
958+ elif d < 0 :
959+ j += 1
960+ else :
961+ return target
962+ return res
963+
859964############# 116. Populating Next Right Pointers in Each Node II #############
860965
861966class Solution :
@@ -892,6 +997,73 @@ def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
892997 node .next = queue [0 ]
893998 return root
894999
1000+ ############# 727. Minimum Window Subsequence ############
1001+ class Solution :
1002+ def minWindow (self , s1 : str , s2 : str ) -> str :
1003+ """
1004+ dp[i][j] stores the LARGEST starting index in s1 of the substring
1005+ where s2 has length i and the window ends at j-1 (s2[:i] is totally included in first j characters s1[:j])
1006+
1007+ So dp[i][j] would be:
1008+ if s2[i - 1] == s1[j - 1], this means we could borrow the start index from dp[i - 1][j - 1] to make the current substring valid;
1009+ else, we only need to borrow the start index from dp[i][j - 1] which could either exist or not.
1010+
1011+ Finally, go through the last row to find the substring with min length and appears first.
1012+ """
1013+ n , m = len (s1 ), len (s2 )
1014+
1015+ # Initialize the DP array, m+1 rows and n+1 columns
1016+ dp = [[- 1 ] * (n + 1 ) for _ in range (m + 1 )] # dp[i][j] stores the LARGEST starting index in s1 of the substring
1017+
1018+ # when s2 is empty string, the min window in s1 that includes it is still empty string
1019+ # by definition, dp[i][j] stores largest index of min window that ends at j-1 (or first j chars s[:j])
1020+ # so the min window that includes empty string AND ends at j-1 starts at (s1[j:j] == "")
1021+ for j in range (n + 1 ):
1022+ dp [0 ][j ] = j # s1[j:j] == "" includes s2[:0] == ""
1023+
1024+ # Fill the dp array
1025+ for i in range (1 , m + 1 ):
1026+ for j in range (1 , n + 1 ):
1027+ # s1[dp[i-1][j-1]:j-1] includes a valid subsequence of s2[:i-1] && s1[j] == s2[i]
1028+ # => s1[dp[i][j]:j] includes a valid subsequence of s2[:i]
1029+ if s2 [i - 1 ] == s1 [j - 1 ]: # current matching s1[j] == s2[i] belongs to same window as s1[j-1] == s2[i-1]
1030+ dp [i ][j ] = dp [i - 1 ][j - 1 ]
1031+ else : # if no match, need stricter condition: find s2[:i] in first j-1 characters of s1. The start index is same if found
1032+ dp [i ][j ] = dp [i ][j - 1 ]
1033+
1034+
1035+ # Now find the minimum length window
1036+ start , length = 0 , n + 1
1037+ for j in range (1 , n + 1 ):
1038+ if dp [m ][j ] != - 1 :
1039+ if j - dp [m ][j ] < length :
1040+ start = dp [m ][j ]
1041+ length = j - dp [m ][j ]
1042+
1043+ return "" if length == n + 1 else s1 [start : start + length ]
1044+
1045+ ############# 543. Diameter of Binary Tree ############
1046+ class Solution :
1047+ def diameterOfBinaryTree (self , root : Optional [TreeNode ]) -> int :
1048+ res = 0
1049+
1050+ def dfs (node ):
1051+ """
1052+ DFS returns the depth of the tree from current root
1053+ Each recursive call updates res with two-way path passing root node
1054+ Leaf node: 0, None node: -1, so adding one to none equals zero (don't contribute to path length)
1055+ """
1056+ nonlocal res
1057+ if not node :
1058+ return - 1
1059+
1060+ left , right = dfs (node .left ), dfs (node .right )
1061+ res = max (res , 2 + left + right ) # maxlen duo-way path passing current node
1062+
1063+ return 1 + max (left , right ) # maxlen single-way path from deepest leave up to current node
1064+
1065+ dfs (root )
1066+ return res
8951067
8961068############# 658. Find K Closest Elements ############
8971069class Solution :
@@ -923,6 +1095,43 @@ def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]:
9231095 left -= 1
9241096 return arr [left :right ]
9251097
1098+ ############# 88. Merge Sorted Array ############
1099+ class Solution :
1100+ def merge (self , nums1 : List [int ], m : int , nums2 : List [int ], n : int ) -> None :
1101+ """
1102+ Do not return anything, modify nums1 in-place instead.
1103+ """
1104+ i , j = m - 1 , n - 1
1105+ k = len (nums1 )- 1
1106+
1107+ while i >= 0 and j >= 0 :
1108+ if nums1 [i ] > nums2 [j ]:
1109+ nums1 [k ] = nums1 [i ]
1110+ i -= 1
1111+ else :
1112+ nums1 [k ] = nums2 [j ]
1113+ j -= 1
1114+
1115+ k -= 1
1116+
1117+ while j >= 0 :
1118+ nums1 [k ] = nums2 [j ]
1119+ k -= 1
1120+ j -= 1
1121+
1122+ ############ 398. Random Pick Index ############
1123+ from collections import defaultdict
1124+ import random
1125+ class Solution :
1126+
1127+ def __init__ (self , nums : List [int ]):
1128+ self .d = defaultdict (list )
1129+ for i , num in enumerate (nums ):
1130+ self .d [num ].append (i )
1131+
1132+ def pick (self , target : int ) -> int :
1133+ return random .choice (self .d [target ])
1134+
9261135############ 865. Smallest Subtree with all the Deepest Nodes ############
9271136############# 1123. Lowest Common Ancestor of Deepest Leaves ############
9281137class Solution :
@@ -966,3 +1175,108 @@ def threeSumClosest(self, nums: List[int], target: int) -> int:
9661175 else :
9671176 return target
9681177 return res
1178+
1179+
1180+ ############# 79. Word Search ############
1181+ DIR = {(0 , 1 ), (0 , - 1 ), (- 1 , 0 ), (1 , 0 )}
1182+ VISITED_SENTINEL = '#' # any character that will not appear in word charset
1183+ class Solution :
1184+ def exist (self , board : List [List [str ]], word : str ) -> bool :
1185+ m , n = len (board ), len (board [0 ])
1186+ def backtrack (x , y , wordi ):
1187+ if wordi == len (word ):
1188+ return True
1189+ if not (0 <= x < m and 0 <= y < n ):
1190+ return False # cannot match if out of bound
1191+ if board [x ][y ] != word [wordi ]:
1192+ return False
1193+
1194+ original = board [x ][y ]
1195+ board [x ][y ] = VISITED_SENTINEL
1196+
1197+ for dx , dy in DIR :
1198+ nx , ny = x + dx , y + dy
1199+ if backtrack (nx , ny , wordi + 1 ):
1200+ board [x ][y ] = original
1201+ return True
1202+
1203+ board [x ][y ] = original
1204+ return False
1205+
1206+ for i in range (m ):
1207+ for j in range (n ):
1208+ if backtrack (i , j , 0 ):
1209+ return True
1210+ return False
1211+
1212+
1213+ ############# 791. Custom Sort String ############
1214+ from collections import Counter
1215+ class Solution :
1216+ def customSortString (self , order : str , s : str ) -> str :
1217+ freq = Counter (s )
1218+ res = ""
1219+ for char in order :
1220+ res += char * freq [char ]
1221+ del freq [char ]
1222+
1223+ for char , f in freq .items ():
1224+ res += char * f
1225+ return res
1226+
1227+ ############# 30. Substring with Concatenation of All Words ############
1228+ # Solution with illustration https://leetcode.com/problems/substring-with-concatenation-of-all-words/solutions/1753357/clear-solution-easy-to-understand-with-diagrams-o-n-x-w-approach/
1229+ class Solution :
1230+ def findSubstring (self , s : str , words : List [str ]) -> List [int ]:
1231+ """
1232+ Let wordlen = len(words[0])
1233+ Time O(len(s) * wordlen)
1234+ Use two hashmaps + two pointers
1235+ - `need` one hashmap to count all frequencies of each word in words
1236+ - `window` one to count current each substring's frequency in current window s[left:left+wordlen*len(words)]
1237+ - `matched_substrs` count how many words in window has been matched
1238+
1239+ We consider all such windows starting from 0, 1, 2, 3, ... wordlen-1, each time moving left/right pointer by wordlen.
1240+ This problem effectively is a combination of wordlen sliding window problems.
1241+ i ---> i+w ---> i+2w ----> i+3w ----> i+4w
1242+ (i+1) ---> (i+1)+w ---> (i+1)+2w ----> (i+1)+3w ----> (i+1)+4w
1243+ (i+2) ---> (i+2)+w ---> (i+2)+2w ----> (i+2)+3w ----> (i+2)+4w
1244+ (i+3) ---> (i+3)+w ---> (i+3)+2w ----> (i+3)+3w ----> (i+3)+4w
1245+
1246+ If there's a mismatch at right pointer, we move left pointer to right of right pointer because
1247+ any window that includes the mismatch is invalid window. Also we need to reset counter to zero
1248+
1249+ If advancing right pointer causes excess of substring in current window, we shrink the window size by wordlen and
1250+ update substring count in `window` hashmap accordingly
1251+ """
1252+ need = Counter (words ) # substr => frequency in `words`
1253+ res = []
1254+ wordlen = len (words [0 ])
1255+
1256+ for k in range (wordlen ):
1257+ window = Counter () # window substr => frequency in s[left:right], right == i + wordlen*len(words)
1258+ left = k
1259+ matched_substrs = 0 # sum of all frequencies in `window`. matched_substrs == sum(window.values())
1260+ for right in range (left , len (s ), wordlen ):
1261+ if right + wordlen > len (s ): # out of bounds, cannot form window
1262+ break
1263+ nextsubstr = s [right :right + wordlen ]
1264+ if nextsubstr in need : # matched
1265+ window [nextsubstr ] += 1
1266+ matched_substrs += 1
1267+ while window [nextsubstr ] > need [nextsubstr ]: # matched, but excess, need move left pointer till no excess
1268+ oldsubstr = s [left :left + wordlen ]
1269+ window [oldsubstr ] -= 1
1270+ matched_substrs -= 1
1271+ left += wordlen
1272+ if matched_substrs == len (words ): # yay! we found a permutation of words
1273+ res .append (left )
1274+ window [s [left :left + wordlen ]] -= 1
1275+ matched_substrs -= 1
1276+ left += wordlen
1277+ else : # mismatch, reset counter, and move left
1278+ window .clear ()
1279+ matched_substrs = 0
1280+ left = right + wordlen
1281+
1282+ return res
0 commit comments