classSolution: defminStickers(self, stickers: List[str], target: str) -> int: """ @param stickers: e.g. ["with","example","science"] @param target: e.g. "thehat" @return: e.g. 3 """ deftrans(s): # 统计s中每个字母在target中被需要的次数 cnts = Counter() for c in s: if c in target: cnts[c] += 1 return cnts availables = [c for st in stickers if (c:=trans(st))] # [Counter({'t': 1, 'h': 1}), Counter({'e': 2, 'a': 1}), Counter({'e': 2})] queue = deque([(target, 0)]) # 维护一个step递增的队列 (先进先出), 也可从BFS的角度理解; 翻译为: 已经使用0个贴纸了, 剩下要拼的字符串为target explored = {target} while queue: cur, step = queue.popleft() # 保证step小的先被处理 ifnot cur: return step for avl in availables: if cur[0] in avl: # !! 最左侧的元素迟早是要被删掉的, 仅仅检查一个字母, 节约时间 nxt = cur for k, v in avl.items(): nxt = nxt.replace(k, '', v) # 删去v个字符k if nxt notin explored: # 相同的nxt, 其step必然相同, 这是避免了重复 explored.add(nxt) queue.append((nxt, step + 1)) # 翻译为: 已经使用了step+1个贴纸了, 剩下的要拼的字符串为nxt return -1
🛠collections.Counter
A Counter
is a dict
subclass for counting hashable objects. It is a collection where
elements are stored as dictionary keys and their counts are stored as
dictionary values. Counts are allowed to be any integer value including
zero or negative counts. The Counter
class is similar to bags or multisets in other languages.
1 2 3 4 5
c = Counter() # Counter() c = Counter('gallahad') # Counter({'g': 1, 'a': 3, 'l': 2, 'h': 1, 'd': 1}) c = Counter({'red': 4, 'blue': 2}) # Counter({'red': 4, 'blue': 2}) c = Counter(cats=4, dogs=8) # Counter({'cats': 4, 'dogs': 8}), count of a missing element is zero del c['cats'] # del removes the entry
🛠Assignment
expressions
There is new syntax := that assigns values to variables
as part of a larger expression. It is affectionately known as “the
walrus operator” due to its resemblance to the
eyes and tusks of a walrus.
💛62. 不同路径
image-20220514173113889
DP - Carlos
1 2 3 4 5 6 7
classSolution: defuniquePaths(self, m: int, n: int) -> int: array = [[1] * n for _ inrange(m)] for i inrange(1, m): for j inrange(1, n): array[i][j] = array[i - 1][j] + array[i][j - 1] return array[-1][-1]
💛64.
最小路径和
与20220514美团第二题"飞行"思路一致.
DP - Carlos
1 2 3 4 5 6 7 8 9 10
classSolution: defminPathSum(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) array = [[grid[0][0]] * n for _ inrange(m)] for _ inrange(1, m): array[_][0] = array[_ - 1][0] + grid[_][0] for _ inrange(1, n): array[0][_] = array[0][_ - 1] + grid[0][_] for i inrange(1, m): for j inrange(1, n): array[i][j] = min(array[i - 1][j], array[i][j - 1]) + grid[i][j] return array[-1][-1]
💛剑指
Offer 47. 礼物的最大价值
DP - Carlos
1 2 3 4 5 6 7 8 9 10
classSolution: defmaxValue(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) array = [[grid[0][0]] * n for _ inrange(m)] for i inrange(1, m): array[i][0] = array[i - 1][0] + grid[i][0] for j inrange(1, n): array[0][j] = array[0][j - 1] + grid[0][j] for i inrange(1, m): for j inrange(1, n): array[i][j] = max(array[i - 1][j], array[i][j - 1]) + grid[i][j] return array[-1][-1]
classSolution: defisAlienSorted(self, words: List[str], order: str) -> bool: index = {c: i for i, c inenumerate(order)} returnall(s <= t for s, t in pairwise([index[c] for c in word] for word in words))
🛠itertools.pairwise
Return successive overlapping pairs taken from the input
iterable.
1 2 3 4 5
defpairwise(iterable): # pairwise('ABCDEFG') --> AB BC CD DE EF FG a, b = tee(iterable) # tee默认参数为2, 这里返回了两个相同的iterators next(b, None) returnzip(a, b)
🛠itertools.tee
Return n independent iterators from a single iterable. See
examples on Python –
Itertools.tee().
[Python/Java/JavaScript/Go]
模拟 - Benhao
按order的规则逐位比较,从左往右只要有一组相邻单词不满足序,直接返回false即可。
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: defisAlienSorted(self, words: List[str], order: str) -> bool: mp = {c: i for i, c inenumerate(order)} for i, w inenumerate(words[:-1]): j = 0# 检查到的相同的字符总数 while j < len(w): # 检查w中的每个字符 if j == len(words[i + 1]) or (a := mp[w[j]] - mp[words[i + 1][j]]) > 0: # 发现下一个词对应位置没有字符, 或不满足字典顺序 returnFalse elif a < 0: # 检查的这一对满足要求了, 检查下一对 break j += 1 returnTrue
1 2 3
classSolution: # 直接对words进行排序, key用的是tuple, 因为tuple的排序方式和题目要求的字典顺序吻合 defisAlienSorted(self, words: List[str], order: str) -> bool: returnsorted(words, key=lambda x:tuple(mp[c] for c in x)) == words if (mp := {c:i for i, c inenumerate(order)}) elseFalse
Pairwise - jerryluan
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: defisAlienSorted(self, words: List[str], order: str) -> bool: mp = {c: i for i, c inenumerate(order)} for w1, w2 in pairwise(words): j = 0# 检查到的相同的字符总数 while j < len(w1): # 检查w1中的每个字符 if j == len(w2) or (a := mp[w1[j]] - mp[w2[j]]) > 0: # 发现下一个词对应位置没有字符, 或不满足字典顺序 returnFalse elif a < 0: # 检查的这一对满足要求了, 检查下一对 break j += 1 returnTrue
💛46. 全排列
20220517快手一面第一题.
DC - Carlos
1 2 3 4 5 6 7 8 9 10
classSolution: defpermute(self, nums: List[int]) -> List[List[int]]: iflen(nums) == 1: return [nums] result = [] for i, n inenumerate(nums): temp = self.permute(nums[:i] + nums[i+1:]) for _ in temp: result.append([n] + _) return result
Locate the insertion point for x in a to maintain
sorted order.
The returned insertion point i partitions the array
a into two halves so that
all(val < x for val in a[lo : i]) for the left side and
all(val >= x for val in a[i : hi]) for the right
side.
理解: 求a中某值使key(a)=x; 给了值域a, 逆映射key,
求接近x的自变量值
bisect_left和bisect_right有什么不同呢?
先给出When are
bisect_left and bisect_right not
equal?的一个修改后的例子如下:
When the target to locate is in the list, bisect_left,
bisect_right return different result. For example:
classSolution: # 问题转化为: 有几个数<=x? 每行有min(n, x//i)个! deffindKthNumber(self, m: int, n: int, k: int) -> int: return bisect_left(range(m * n + 1), k, key=lambda x:sum(min(n, x // i) for i inrange(1, m + 1))) if n >= m else self.findKthNumber(n, m, k) # 交换行数,对小的行数统计二分值
classSolution: # 遍历每个数, 然后二分找第二个数 deftwoSum(self, nums: List[int], target: int) -> List[int]: for i inrange(len(nums)): temp = nums[:i] + nums[i+1:] second = bisect_left(temp, target - nums[i]) if second < len(temp) and temp[second] == target - nums[i]: # 第一个条件是为了防止二分超出索引限制 return [nums[i], temp[second]]
面试题57.
和为 s 的两个数字(双指针 + 证明,清晰图解) - Krahets
1 2 3 4 5 6 7 8 9
classSolution: # 对撞双指针 deftwoSum(self, nums: List[int], target: int) -> List[int]: i, j = 0, len(nums) - 1 while i < j: s = nums[i] + nums[j] if s > target: j -= 1 elif s < target: i += 1 else: return nums[i], nums[j] return []
💛102.
二叉树的层序遍历
队列 - Carlos
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution: # 写得难看的队列 deflevelOrder(self, root: TreeNode) -> List[List[int]]: result = [] visited = [root] # 储存每层要扫的节点 while visited andnotall([_ isNonefor _ in visited]): temp = [] result.append([_.val for _ in visited if _]) for r in visited: if r: # 这一步不能保证加入的不是None, 污染了visited!! temp.append(r.left) temp.append(r.right) visited = temp return result
classSolution: defisSymmetric(self, root: Optional[TreeNode]) -> bool: ifnot root: returnTrue lefts, rights = [root.left], [root.right] while lefts and rights: left, right = lefts.pop(), rights.pop() # pop出末尾元素, 这是把list当堆栈了 if left isNoneand right isNone: continue# 扫到了两个叶子 if left isNoneor right isNone: returnFalse# 发现形状不对称 if left.val != right.val: returnFalse# 发现值不对称 lefts.append(left.left) rights.append(right.right) lefts.append(left.right) rights.append(right.left) returnTrue
动画演示+多种实现
101. 对称二叉树 - 王尼玛
1 2 3 4 5 6 7 8 9
classSolution: # 递归 defisSymmetric(self, root: Optional[TreeNode]) -> bool: ifnot root: returnTrue deff(left, right): if left isNoneand right isNone: returnTrue if left isNoneor right isNone: returnFalse if left.val != right.val: returnFalse return f(left.left, right.right) and f(left.right, right.left) return f(root.left, root.right)
classSolution: # 时间复杂度O(n), 空间复杂度O(n) defrepeatedNTimes(self, nums: List[int]) -> int: found = set() # 利用2n的列表里有n个相同的数, n+1个不同的数的性质, 维护一个已查看的集合 for num in nums: if num in found: return num found.add(num) return -1
classSolution: # 时间复杂度O(n), 空间复杂度O(1) defrepeatedNTimes(self, nums: List[int]) -> int: if nums[0] == nums[3]: # 处理n=2的特殊情况 return nums[0] for i, num inenumerate(nums): if nums[i + 1] == num or nums[i + 2] == num: # 检查其后间隔为0和1的元素 return num return -1
classSolution: # 时间复杂度O(1), 空间复杂度O(1) defrepeatedNTimes(self, nums: List[int]) -> int: n = len(nums) whileTrue: x, y = random.randrange(n), random.randrange(n) if x != y and nums[x] == nums[y]: return nums[x]
classSolution: deftotalStrength(self, strength: List[int]) -> int: summation_dict = {(_, _ + 1): strength[_] for _ inrange(len(strength))} minimum_dict = {(_, _ + 1): strength[_] for _ inrange(len(strength))} result = 0 for l inrange(1, len(strength) + 1): for i inrange(len(strength) - l + 1): temp = strength[i: i+l] if (i, i + l) in summation_dict: temp_sum = summation_dict[(i, i + l)] temp_min = minimum_dict[(i, i + l)] else: if (i + 1, i + l) in summation_dict: temp_sum = summation_dict[(i + 1, i + l)] + strength[i] temp_min = min(minimum_dict[(i + 1, i + l)], strength[i]) elif (i, i + l - 1) in summation_dict: temp_sum = summation_dict[(i, i + l - 1)] + strength[i + l - 1] temp_min = min(minimum_dict[(i, i + l - 1)], strength[i + l - 1]) else: assertFalse, (i, i + l, summation_dict) summation_dict[(i, i + l)] = temp_sum minimum_dict[(i, i + l)] = temp_min result += temp_min * temp_sum return result % (1000000007)
classSolution: defisUnivalTree(self, root: TreeNode) -> bool: ifnot root or root.left == root.right == None: returnTrue if self.isUnivalTree(root.left) and self.isUnivalTree(root.right): if root.left and root.right: return root.val == root.left.val == root.right.val if root.left: return root.val == root.left.val return root.val == root.right.val returnFalse
[Python/Java/TypeScript/Go]
模拟 - Benhao
1 2 3 4 5 6
classSolution: # dfs defisUnivalTree(self, root: TreeNode) -> bool: v = root.val # !! 这一步省去了判断两个孩子节点时, 要判断两个孩子节点的值是否相同 defdfs(node): # 首先判断当前点的值是否是v, 随后分别判断两个孩子节点, 分别判断是也会各判断孩子节点的值是否是v return node isNoneor (node.val == v and dfs(node.left) and dfs(node.right)) return dfs(root)
1 2 3 4 5 6 7 8 9
classSolution: # 迭代器写法, 前中后序的遍历输出可以参照这种方式写出 defisUnivalTree(self, root: TreeNode) -> bool: v = root.val defdfs(node): if node: yield node.val yieldfrom dfs(node.left) yieldfrom dfs(node.right) returnall(v == other for other in dfs(root))
🛠Yield
expressions
What does the
"yield" keyword do?: 从易到难, 逻辑清晰的迭代器解释
In practice,
what are the main uses for the "yield from" syntax in Python 3.3?:
What yield from does is it establishes a transparent
bidirectional connection between the caller and the sub-generator.
(yield from的作用是它在调用者和子生成器之间建立一个透明的双向连接.)
classSolution: # 对与过大的方块, 阶梯函数的求最大和更新太慢 deffallingSquares(self, positions: List[List[int]]) -> List[int]: max_len = max(_[0] + _[1] for _ in positions) f = [0] * max_len # step function (阶梯函数) result, max_height = [], 0 for x, l in positions: idxes = range(x, x + l) # 加入方块后, 会影响的区间 max_f = max(f[_] for _ in idxes) # 找该区间内阶梯函数的最大值 for idx in idxes: f[idx] = max_f + l # 更新阶梯函数 max_height = max(max_f + l, max_height) # 找阶梯函数的全局最大值 result.append(max_height) return result
掉落的方块
- 力扣官方题解
方法一: 暴力枚举 (时O2空O1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution: deffallingSquares(self, positions: List[List[int]]) -> List[int]: n = len(positions) heights = [0] * n for i, (left1, side1) inenumerate(positions): right1 = left1 + side1 # left和right分别是正方形边缘在数轴上对应的坐标 heights[i] = side1 # 初始化高度为自己的高度 for j inrange(i): # 遍历之前放下的每个方块 left2, right2 = positions[j][0], positions[j][0] + positions[j][1] if right1 > left2 and right2 > left1: # 若相交, 注意取等号时是正好放下的情况 heights[i] = max(heights[i], heights[j] + side1) # 若重叠有多个方块, 要逐个检查 for i inrange(1, n): # 之前算的是放下所在区间的局部高度, 要对前面所有方块的高度取最大 heights[i] = max(heights[i], heights[i - 1]) # !! 注意只需对前一个取最大 return heights
方法二: 有序集合
(时Onlogn空On)
💛面试题 17.11.
单词距离
Dict - Carlos
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution: # 创建一个字典储存每个单词出现的位置, 然后做一个二重循环查找最小距离 deffindClosest(self, words: List[str], word1: str, word2: str) -> int: temp = dict() for i, _ inenumerate(words): if _ in temp: temp[_].append(i) else: temp[_] = [i] result = len(words) for i in temp[word1]: for j in temp[word2]: ifabs(i - j) < result: result = abs(i - j) return result
[Python/Java/TypeScript/Go]
模拟
1 2 3 4 5 6 7 8 9 10
classSolution: # 对每个字符串依次存储他们的全部坐标即可反复比较不同的单词的距离 deffindClosest(self, words: List[str], word1: str, word2: str) -> int: ans, idx1, idx2 = inf, inf, -inf for i, word inenumerate(words): if word1 == word: idx1 = i elif word2 == word: idx2 = i ans = min(ans, abs(idx1 - idx2)) return ans
💚1021.
删除最外层的括号
Loop - Carlos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution: defremoveOuterParentheses(self, s: str) -> str: result = '' start, temp = 0, 0# start表明是否将正在检查的字符加入result, temp用来检查待打印的括号是否匹配 for i, _ inenumerate(s): if start == 0and _ == '(': # 表明开始打印后续的字符 start = 1 else: if temp == 0and _ == ')': # 表明结束打印, 初始化start start = 0 continue if _ == '(': result += _ temp += 1 elif _ == ')': result += _ temp -= 1 return result
classSolution: defremoveOuterParentheses(self, s: str) -> str: res, stack = "", [] # stack至多只有一个字符'(' for c in s: if c == ')': stack.pop() # 让stack空, 停止输出 if stack: res += c if c == '(': stack.append(c) # 让stack非空知道有匹配的) return res
方法二: 计数 (时On空On)
1 2 3 4 5 6 7 8
classSolution: defremoveOuterParentheses(self, s: str) -> str: res, level = "", 0 for c in s: if c == ')': level -= 1 if level: res += c if c == '(': level += 1 return res
🎯第 79 场双周赛 -
极氪智能科技
12 / 18, 824 / 4250.
💚2283.
判断一个数的数字计数是否等于数位的值
1 2 3 4 5 6 7
classSolution: defdigitCount(self, num: str) -> bool: temp = Counter(num) for i, n inenumerate(num): ifnotint(n) == temp[str(i)]: returnFalse returnTrue
## 💛2284.
最多单词数的发件人
1 2 3 4 5 6 7
classSolution: deflargestWordCount(self, messages: List[str], senders: List[str]) -> str: d = {s:0for s in senders[::-1]} for i, s inenumerate(senders): d[s] += len(messages[i].split()) targets = [k for k, v in d.items() if v == max(d.values())] returnmax(targets)
💛2285.
道路的最大总重要性
1 2 3 4 5 6 7 8
classSolution: defmaximumImportance(self, n: int, roads: List[List[int]]) -> int: degrees = [0] * n for x, y in roads: degrees[x] += 1 degrees[y] += 1 degrees = sorted(degrees) returnsum((_ + 1) * degrees[_] for _ inrange(n))
classSolution: defalienOrder(self, words: List[str]) -> str: # input: ["wrt","wrf","er","ett","rftt"], output: "wertf" g = {} for c in words[0]: g[c] = [] # 初始化第一个word for s, t in pairwise(words): for c in t: g.setdefault(c, []) # 初始化后len(words)-1个word # In Python Dictionary, setdefault() method returns the value of a key (if the key is in dictionary). If not, it inserts key with a value to the dictionary. for u, v inzip(s, t): # 注意zip对长度不一word的效果, s=wrf, t=er, list(zip(s, t))=[('w', 'e'), ('r', 'r')] if u != v: # 字典排序依据1是第一个不同的字幕 g[u].append(v) # 有向边u→v break else: iflen(s) > len(t): # 字典排序依据2是长度, 短的要排在前面 return"" # g为{'w': ['e'], 'r': ['t'], 't': ['f'], 'f': [], 'e': ['r']}, g至多有len(words)-1条边 VISITING, VISITED = 1, 2 states = {} order = [] defdfs(u: str) -> bool: states[u] = VISITING for v in g[u]: if v notin states: # 深度优先 ifnot dfs(v): returnFalse elif states[v] == VISITING: # 有圈 returnFalse # else, v状态为VISITED, 不做任何操作 order.append(u) states[u] = VISITED # u的所有邻居都是VISITED, 此时把u置为VISITED returnTrue return''.join(reversed(order)) ifall(dfs(u) for u in g if u notin states) else""