Leetcode
- [30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
- [798. 得分最高的最小轮调](https://leetcode-cn.com/problems/smallest-rotation-with-highest-score/)
- 1601. 最多可达成的换楼请求数目
- 564. 寻找最近的回文数
- [1719. 重构一棵树的方案数](https://leetcode-cn.com/problems/number-of-ways-to-reconstruct-a-tree/)
- [2019. 解出数学表达式的学生分数](https://leetcode-cn.com/problems/the-score-of-students-solving-math-expression/)
30. 串联所有单词的子串
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
## 方法一:全排列 超时
# n = len(words)
# res = []
# for p in permutations(words, n):
# i = "".join(p)
# idx = -1
# while True:
# idx = s.find(i, idx + 1)
# if idx == -1:break
# res.append(idx)
# return list(set(res))
## 方法二:Counter
# from collections import Counter
def f(x):
d = defaultdict(int)
for s in x: d[s] += 1
return d
res = []
wordLen = len(words[0])
totalLen = len(words) * wordLen
n = len(s)
words = f(words)
for i in range(n - totalLen + 1):
sub = s[i:i + totalLen] # 依次截取总长子串,移动窗口。
tmp = [sub[j:j + wordLen] for j in range(0, totalLen, wordLen)] # 切割总子串为等长子串
if f(tmp) == words: # 单词与个数都相同,正好匹配。
res.append(i)
return res
798. 得分最高的最小轮调
最简单的做法是遍历每个可能的 k,计算轮调 k 个位置之后的数组得分。假设数组的长度是 n,则有 n 种可能的轮调,对于每种轮调都需要 O(n) 的时间计算得分,总时间复杂度是 O(n2),对于 n ≤ 105 的数据范围会超出时间限制,因此需要优化。
class Solution:
def bestRotation(self, nums: List[int]) -> int:
n, a, score, index = len(nums), nums * 2, 0, 0
for k in range(n):
t = sum(e <= i for i, e in enumerate(a[k : n + k]))
if t > score: index, score = k, t
return index
nums[i] = x,当 i ≥ x,即 i ∈ [x, n) 时,得 1 分。
轮调 k 次后,下标为 i - k,当新下标为负数时,相当于 nums[i] 出现在比原数组更“靠后”的位置,此时下标等价于 (i - k + n) % n。
class Solution:
def bestRotation(self, nums: List[int]) -> int:
n = len(nums)
diffs = [0] * n
for i, num in enumerate(nums):
low = (i + 1) % n
high = (i - num + n + 1) % n
diffs[low] += 1
diffs[high] -= 1
if low >= high:
diffs[0] += 1
score, maxScore, idx = 0, 0, 0
for k, diff in enumerate(diffs):
score += diff
if score > maxScore:
maxScore, idx = score, k
return idx
1601. 最多可达成的换楼请求数目
Leetcode 过滤掉同楼的请求
从大到小枚举最多成立的请求数
class Solution:
def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
m = len(requests)
requests = list(filter(lambda x:x[0] != x[1], requests))
m -= len(requests)
for i in range(len(requests), 0, -1):
# 从 requests 里选取 i 个的所有组合
for comb in combinations(requests, i):
# 统计出度入度是否完全相等
cnts = [0] * n
for a, b in comb:
cnts[a] += 1 # 出度
cnts[b] -= 1 # 入度
if all(not c for c in cnts):
return i + m
return m
## 方法二:二进制枚举
# 用一个长度为 m 的二进制数 mask 表示所有的请求,
# 其中 mask 从低到高的第 i 位为 1 表示选择第 i 个请求,为 0 表示不选第 i 个请求。
# 枚举 [0, 2^m-1] 范围内的所有 mask,对于每个mask,依次枚举其每一位,判断是否为 1,
# 并使用与方法一相同的数组 delta 以及变量 cnt 进行统计,在满足要求时更新答案。
ans = 0
for mask in range(1 << len(requests)):
cnt = mask.bit_count() # 选择数
if cnt <= ans: continue
delta = [0] * n
for i, (x, y) in enumerate(requests):
if mask & (1 << i):
delta[x] += 1
delta[y] -= 1
if all(x == 0 for x in delta):
ans = cnt
return ans + m
564. 寻找最近的回文数
Leetcode 小于等于10 的数,返回 n-1
10 的幂,返回 n-1
若干个 9,返回 n+2
11,这个数字比较特殊,返回 9
首先把 n 从中间分成 a、b 两部分,如果长度是奇数就给 a 多分点。
然后用 a、a+1、a-1 为左边分别构建一个回文数,注意n长度为奇数的时候左边的最后一个字符不能复制过去。
最后选取离 n 最近且不为 n 的结果即可
class Solution:
def nearestPalindromic(self, n: str) -> str:
if int(n) < 10 or int(n[::-1]) == 1:
return str(int(n) - 1)
if n == '11': return '9'
if set(n) == {'9'}: return str(int(n) + 2)
x = (len(n) + 1) // 2
a, b = n[:x], n[x:] # 偶数对半,奇数 a 少 b 多
temp = [str(int(a) - 1), a, str(int(a) + 1)]
temp = [i + i[len(b) - 1::-1] for i in temp]
return min(temp, key = lambda x:abs(int(x) - int(n)) or float('inf'))
1719. 重构一棵树的方案数
2019. 解出数学表达式的学生分数
op = {'+': add, '*': mul}
@lru_cache(None)
def dfs(s):
return {s[0]} if len(s) == 1 else\
set([op[s[i]](a, b) for i in range(1, len(s), 2) for a in dfs(s[:i]) for b in dfs(s[i + 1:]) if op[s[i]](a, b) <= 1000])
class Solution:
def scoreOfStudents(self, s: str, answers: List[int]) -> int:
correct = eval(s)
d = dfs(tuple(ch if ch in ('+', '*') else int(ch) for ch in s))
return sum(5 if x == correct else 2 for x in answers if x in d)