class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
countS = {}
for c in s:
if c not in countS:
countS[c] = 0
countS[c] += 1
countT = {}
for c in t:
if c not in countT:
countT[c] = 0
countT[c] += 1
for k in countS.keys():
if (k not in countT) or (countS[k] != countT[k]):
return False
return True
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
hashmap = {}
N = len(nums)
for n in nums:
if n not in hashmap:
hashmap[n] = 0
hashmap[n] += 1
arr = [[] for _ in range(N + 1)]
for keyy, v in hashmap.items():
arr[v].append(keyy)
res = []
r = len(nums)
while -1 < r:
for n in arr[r]:
res.append(n)
if len(res) == k:
return res
r -= 1
return res
class Solution:
def encode(self, strs: List[str]) -> str:
res = ''
for s in strs:
res += str(len(s)) + '#' + s
return res
def decode(self, s: str) -> List[str]:
i = 0
N = len(s)
res = []
while i < N:
j = i
while s[j+1] != '#':
j += 1
n = int(s[i:j+1])
res.append(s[j+2:j+2+n])
i = j + n + 2
# j -> at length of n
# j+1 -> at '#'
# j+2 -> word 1st index
return res
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
N = len(nums)
res = []
for i in range(N-2):
if i != 0 and nums[i-1] == nums[i]: continue
l, r = i+1, N-1
while l < r:
v = nums[i] + nums[l] + nums[r]
if v < 0:
l += 1
elif 0 < v:
r -= 1
else:
res.append([nums[i], nums[l], nums[r]])
while l + 1 < r and nums[l] == nums[l+1]:
l += 1
l += 1
return res
class Solution:
def minWindow(self, s: str, t: str) -> str:
hashmapT = Counter(t)
res = s+'a'
l = 0
hashmapC = Counter()
needToMatch = len(hashmapT)
for r in range(len(s)):
hashmapC[s[r]] += 1
needToMatch -= hashmapC[s[r]] == hashmapT[s[r]]
while needToMatch == 0:
if r - l + 1 < len(res):
res = s[l:r+1]
needToMatch += hashmapC[s[l]] == hashmapT[s[l]]
hashmapC[s[l]] -= 1
l += 1
return res if len(res) != len(s)+1 else ''
class Solution:
def search(self, nums: List[int], target: int) -> int:
l, r = 0, len(nums)-1
while l <= r:
mid = (l+r)//2
if nums[mid] == target: return mid
if nums[l] <= nums[mid]: #left sorted [1,2,3,4] or [2,3,4,1]
if nums[l] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else: # right sorted [4, 5, 1, 2, 3] or [5, 1, 2, 3, 4]
if nums[mid] < target <= nums[r] :
l = mid + 1
else:
r = mid - 1
return -1
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
head = cur = ListNode()
while list1 and list2:
if list1.val < list2.val:
cur.next = list1
list1 = list1.next
else:
cur.next = list2
list2 = list2.next
cur = cur.next
if list1:
cur.next = list1
if list2:
cur.next = list2
return head.next
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
prev, cur = None, slow.next
slow.next = None
while cur:
temp = cur.next
cur.next = prev
prev = cur
cur = temp
first, second = head, prev
while first and second:
temp1, temp2 = first.next, second.next
first.next = second
second.next = temp1
first, second = temp1, temp2
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if len(lists) < 2:
return lists[0] if lists else None
mid = len(lists) // 2
left = self.mergeKLists(lists[:mid])
right = self.mergeKLists(lists[mid:])
return self.merge(left, right)
def merge(self, a, b):
cur = Dummy = ListNode()
while a and b:
if a.val > b.val:
cur.next = b
b = b.next
else:
cur.next = a
a = a.next
cur = cur.next
if a:
cur.next = a
if b:
cur.next = b
return Dummy.next
class Solution:
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
def dfs(a, b):
if not b:
return True
if not a:
return False
if a.val == b.val and self.isSameTree(a, b):
return True
return dfs(a.left, subRoot) or dfs(a.right, subRoot)
return dfs(root, subRoot)
def isSameTree(self, a, b):
if not a and not b:
return True
if a and b and a.val == b.val:
return self.isSameTree(a.left, b.left) and self.isSameTree(a.right, b.right)
return False
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
self.p, self.q = min(p.val, q.val), max(p.val, q.val)
def dfs(node):
if self.p <= node.val <= self.q:
return node
if node.val < self.p:
return dfs(node.right)
return dfs(node.left)
return dfs(root)
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
q = deque()
q.append(root)
res = []
while q:
sub = []
for _ in range(len(q)):
cur = q.popleft()
sub.append(cur.val)
if cur.left:
q.append(cur.left)
if cur.right:
q.append(cur.right)
res.append(sub)
return res
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
H = {n:i for i, n in enumerate(inorder)}
N = len(preorder)
def dfs(start, end, i):
if start > end:
return None, i
node = TreeNode(preorder[i])
indx = H[node.val]
node.left, i = dfs(start, indx-1, i + 1)
node.right, i = dfs(indx+1,end, i)
return node, i
return dfs(0, N-1, 0)[0]
class Codec:
def serialize(self, root):
res = []
def dfs(node):
if not node:
res.append('N')
return
res.append(str(node.val))
dfs(node.left)
dfs(node.right)
dfs(root)
return ','.join(res)
def deserialize(self, data):
data = data.split(',')
self.i = 0
def dfs():
if data[self.i] == 'N':
self.i += 1
return
node = TreeNode(int(data[self.i]))
self.i += 1
node.left = dfs()
node.right = dfs()
return node
return dfs()
class MedianFinder:
def __init__(self):
self.left = []
self.right = []
def addNum(self, num: int) -> None:
heappush(self.left, -num)
if len(self.left) - 1 > len(self.right):
n = -heappop(self.left)
heappush(self.right, n)
if self.right and self.right[0] < -self.left[0]:
n1 = heappop(self.left)
n2 = heappop(self.right)
heappush(self.left, -n2)
heappush(self.right, -n1)
def findMedian(self) -> float:
if (len(self.left) + len(self.right)) % 2: #Odd
return -self.left[0]
return (-self.left[0] + self.right[0])/2
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
candidates.sort()
def dfs(i, curSum, sub):
if curSum >= target or i >= len(candidates):
if curSum == target:
res.append(sub[:])
return
dfs(i, curSum + candidates[i], sub + [candidates[i]])
dfs(i+1, curSum, sub)
dfs(0, 0, [])
return res
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
row, col = len(board), len(board[0])
N = len(word)
def go(i, r, c):
if i == N:
return True
if not(-1 < r < row and -1 < c < col and board[r][c] == word[i]):
return False
ch = board[r][c]
board[r][c] = "-"
res = go(i+1, r-1,c) or go(i+1, r,c+1) or go(i+1, r,c-1) or go(i+1, r+1,c)
board[r][c] = ch
return res
for r in range(row):
for c in range(col):
if go(0, r, c):
return True
return False
class Trie:
def __init__(self):
self.root = {}
def insert(self, word: str) -> None:
cur = self.root
for c in word:
if c not in cur:
cur[c] = {}
cur = cur[c]
cur["#"] = {}
def search(self, word: str) -> bool:
cur = self.root
for c in word:
if c not in cur:
return False
cur = cur[c]
return '#' in cur
def startsWith(self, prefix: str) -> bool:
cur = self.root
for c in prefix:
if c not in cur:
return False
cur = cur[c]
return True
class WordDictionary:
def __init__(self):
self.root = {}
def addWord(self, word: str) -> None:
cur = self.root
for c in word:
if c not in cur:
cur[c] = {}
cur = cur[c]
cur['1'] = {}
def search(self, word: str) -> bool:
def dfs(i, cur):
if i == len(word):
return '1' in cur
if word[i] == '.':
for k in cur:
if dfs(i+1, cur[k]):
return True
return False
return word[i] in cur and dfs(i+1, cur[word[i]])
return dfs(0, self.root)
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
A = {}
for w in words:
cur = A
for c in w:
if c not in cur:
cur[c] = {}
cur = cur[c]
cur['#'] = w
def dfs(r, c, cur):
if not (-1 < r < row and -1 < c < col and board[r][c] in cur):
return []
ch = board[r][c]
board[r][c] = '-'
res = []
if '#' in cur[ch]:
res += [cur[ch]['#']]
for dr, dc in [(r-1, c), (r, c+1), (r+1, c), (r, c-1)]:
res += dfs(dr, dc, cur[ch])
board[r][c] = ch
return res
row, col = len(board), len(board[0])
res = []
for r in range(row):
for c in range(col):
res += dfs(r, c, A)
return list(set(res))
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
row, col = len(grid), len(grid[0])
res = 0
def dfs(r, c):
if not (-1 < r < row and -1 < c < col and grid[r][c] == '1'):
return
grid[r][c] = '0'
for dr, dc in [(r-1, c), (r, c+1), (r+1, c), (r, c-1)]:
dfs(dr, dc)
for r in range(row):
for c in range(col):
if grid[r][c] == '1':
res += 1
dfs(r, c)
return res
class Solution:
def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
visitedA = set()
visitedP = set()
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
def dfs(prev, r, c, state):
if not(-1 < r < row and -1 < c < col and (r, c) not in state and heights[r][c] >= prev):
return
prev = heights[r][c]
state.add((r, c))
for dr, dc in directions:
dfs(prev, dr+r, dc+c, state)
row, col = len(heights), len(heights[0])
for r in range(row):
dfs(-inf, r, 0, visitedP)
dfs(-inf, r, col-1, visitedA)
for c in range(col):
dfs(-inf, 0, c, visitedP)
dfs(-inf, row-1, c, visitedA)
res = []
for k in visitedA:
if k in visitedP:
res.append(k)
return res
class Solution:
def canFinish(self, N: int, pre: List[List[int]]) -> bool:
adj = defaultdict(list)
for u, v in pre:
adj[u].append(v)
visited = [N+1] * N
def dfs(node):
if visited[node] == N+1:
visited[node] = False #cycle
for nei in adj[node]:
if not dfs(nei):
return False
visited[node] = True #valid
return visited[node]
for n in range(N):
if not dfs(n):
return False
return True
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
adj = defaultdict(list)
for u, v in edges:
adj[u].append(v)
adj[v].append(u)
visited = set()
q = [[-1, 0]] # par, node
while q:
newQ = []
for par, node in q:
if node in visited:
return False
visited.add(node)
for nei in adj[node]:
if par == nei: continue
newQ.append([node, nei])
q = newQ
return len(visited) == n
class Solution:
def countComponents(self, n: int, edges: List[List[int]]) -> int:
adj = [[] for _ in range(n)]
visit = [False] * n
for u, v in edges:
adj[u].append(v)
adj[v].append(u)
def dfs(node):
for nei in adj[node]:
if not visit[nei]:
visit[nei] = True
dfs(nei)
res = 0
for node in range(n):
if not visit[node]:
visit[node] = True
dfs(node)
res += 1
return res
class Solution:
def foreignDictionary(self, words: List[str]) -> str:
adj = defaultdict(set)
for i in range(len(words) - 1):
w1, w2 = words[i], words[i + 1]
minLen = min(len(w1), len(w2))
if len(w1) > len(w2) and w1[:minLen] == w2[:minLen]:
return ""
for j in range(minLen):
if w1[j] != w2[j]:
adj[w1[j]].add(w2[j])
break
visited = {}
res = []
def dfs(char):
if char in visited:
return visited[char]
visited[char] = True
for neighChar in adj[char]:
if dfs(neighChar):
return True
visited[char] = False
res.append(char)
for char in adj:
if dfs(char):
return ""
res.reverse()
return "".join(res)
class Solution:
def longestPalindrome(self, s: str) -> str:
def check(l, r):
while -1 < l and r < N and s[l] == s[r]:
l -= 1
r += 1
return l+1, r-1
res = ''
N = len(s)
for i in range(N):
l, r = check(i, i)
if len(res) < r - l + 1:
res = s[l:r+1]
l, r = check(i, i+1)
if len(res) < r - l + 1:
res = s[l:r+1]
return res
class Solution:
def longestCommonSubsequence(self, t1: str, t2: str) -> int:
dp = [[0] * (len(t2)+1) for _ in range(len(t1)+1)]
for i in range(len(t1)-1, -1, -1):
for j in range(len(t2)-1, -1, -1):
if t1[i] == t2[j]:
dp[i][j] = 1 + dp[i+1][j+1]
else:
dp[i][j] = max(dp[i][j+1], dp[i+1][j])
return dp[0][0]