继续leetcode刷题生涯
这里记录的都是笔者觉得有点意思的做法
参考了好几位大佬的题解,感谢各位大佬
class Solution:
def minSwap(self, A: List[int], B: List[int]) -> int:
length = len(A)
dp_swap = [float("inf")] * length
dp_keep = [float("inf")] * length
# 初始化
dp_swap[0] = 1
dp_keep[0] = 0
# 状态转移
for i in range(1, length):
if A[i] > A[i - 1] and B[i] > B[i - 1]:
dp_swap[i] = dp_swap[i - 1] + 1
dp_keep[i] = dp_keep[i - 1]
if A[i] > B[i - 1] and B[i] > A[i - 1]:
dp_swap[i] = min(dp_keep[i - 1] + 1, dp_swap[i])
dp_keep[i] = min(dp_swap[i - 1], dp_keep[i])
# 输出结果
return min(dp_keep[-1], dp_swap[-1])
# 拓扑排序
class Solution:
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
import collections
res = []
idx = collections.defaultdict(list) #入度表
for i in range(len(graph)):
if not graph[i]: #无出度的加入输出
res += [i]
for j in graph[i]: #生成入度表
idx[j] += [i]
for j in res: #遍历无出度点
for i in idx[j]: #遍历该点的入度点
graph[i].remove(j) #删除出度
if not graph[i]: #如果没有出度了
res += [i] #加入输出
return sorted(res)
class DSU:
def __init__(self, R, C):
#R * C is the source, and isn't a grid square
self.par = list(range(R*C + 1)) # [0, 1, 2, 3, 4, 5, 6, 7, 8],并查集初始化各自为各自的集合
self.rnk = [0] * (R*C + 1) # 相当于合并次数
self.sz = [1] * (R*C + 1) # 表示集合中节点的数目,由于初始化每个节点都在自己的集合,所以都是1
def find(self, x):
if self.par[x] != x: # find终止条件就是一直向上find,直到parent[x]=x,自己就是代表节点
self.par[x] = self.find(self.par[x]) # 递归查找
return self.par[x]
def union(self, x, y):
xr, yr = self.find(x), self.find(y)
if xr == yr: return
if self.rnk[xr] < self.rnk[yr]:
xr, yr = yr, xr
if self.rnk[xr] == self.rnk[yr]:
self.rnk[xr] += 1
self.par[yr] = xr
self.sz[xr] += self.sz[yr] # 集合节点数量合并
def size(self, x):
return self.sz[self.find(x)] # 表示集合中节点的数目
def top(self): # 求顶部那个集合的大小
# Size of component at ephemeral "source" node at index R*C,
# minus 1 to not count the source itself in the size
return self.size(len(self.sz) - 1) - 1 # len(self.sz) - 1其实就是R*C
class Solution(object):
def hitBricks(self, grid, hits):
R, C = len(grid), len(grid[0])
def index(r, c): # 将坐标(x,y)转化为index
return r * C + c
def neighbors(r, c): # 迭代生成符合题意的邻居节点
for nr, nc in ((r-1, c), (r+1, c), (r, c-1), (r, c+1)):
if 0 <= nr < R and 0 <= nc < C:
yield nr, nc
A = [row[:] for row in grid] # [[1, 0, 0, 0], [1, 1, 1, 0]],其实就是grid,相当于深拷贝了
for i, j in hits: # 先去掉hits里面所有位置的砖头
A[i][j] = 0
dsu = DSU(R, C) # 下面开始构造不考虑hits情况下的并查集
for r, row in enumerate(A):
for c, val in enumerate(row): # (r,c)相当于位置,val为当前位置的值
if val: # 该位置有砖头
i = index(r, c) # 坐标转换为index,因为使用list实现的并查集
if r == 0: # 是第一行顶部
dsu.union(i, R*C) # 合并到R*C一派,R*C是标记的顶部集合,我们定义的,因为最大index是R*C-1,设成别的也ok
if r and A[r-1][c]: # 不是第一行并且上一行有砖头
dsu.union(i, index(r-1, c)) # 合并到上一行那一派
if c and A[r][c-1]: # 不是最左边并且左边有砖头
dsu.union(i, index(r, c-1)) # 合并到最左边
# print(dsu.par) # [0, 1, 2, 3, 4, 6, 6, 7, 0] # 这样看并查集内容,你可以放在上面循环里面打印
ans = []
for r, c in reversed(hits): # 反向思考,如果我们是往上放砖,
# 为什么要反向?因为如果是拿砖,后一次会影响前一次,放砖就是前一次被后一次影响
pre_roof = dsu.top() # 保存并查集现在的状态
if grid[r][c] == 0: # 此处无砖,结果不变
ans.append(0)
else:
i = index(r, c) # 若此处有砖,求坐标
for nr, nc in neighbors(r, c): # 遍历四周连接砖头
if A[nr][nc]: # 如果有砖
dsu.union(i, index(nr, nc)) #归到一类
if r == 0:
dsu.union(i, R*C) # 如果是顶部,归到顶部自定义的R*C类
A[r][c] = 1 # 还原A中此处的砖头,A之前是不考虑hits处有砖的grid,必须要还原,我们是在A上计算的,也就是说hits中后面的会影响前面的
ans.append(max(0, dsu.top() - pre_roof - 1)) # 变化后状态-变化前的状态-当前砖块的1,因为这个砖是hits加上去的
return ans[::-1] # 结果反转,因为前面hit是反转的
if __name__ == "__main__":
grid = [[1, 0, 0, 0], [1, 1, 1, 0]]
hits = [[1, 0]]
s = Solution()
print(s.hitBricks(grid, hits))
class Solution:
def uniqueMorseRepresentations(self, words: List[str]) -> int:
dic = {'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.', 'f': '..-.', 'g': '--.', 'h': '....',
'i': '..', 'j': '.---', 'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---', 'p': '.--.',
'q': '--.-', 'r': '.-.', 's': '...', 't': '-', 'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-',
'y': '-.--', 'z': '--..'}
res = set()
for word in words:
tmp = ''
for i in word:
tmp += dic[i]
res.add(tmp)
return len(res)
class Solution:
def splitArraySameAverage(self, A: List[int]) -> bool: # 每个列表的平均数与A的平均数相等
A.sort()
n = len(A)
if n < 2: return False
ave = sum(A) / n
def findlist(A, i, s):
ll = len(A)
if i == 0 and s == 0:
return True
if i <= 0:
return False
for j in range(ll - i + 1): # 保证第一个数字位置加上总位数小于A的长度
if j > 0 and A[j] == A[j - 1]: # 除去重复的情况
continue
if findlist(A[j + 1:], i - 1, s - A[j]):
return True
return False
for i in range(1, n // 2 + 1): # 列表长度
if abs(i * ave - int(i * ave)) > 1e-10: # 整数和必须为整数 可能引入无限小数
continue
if findlist(A, i, int(i * ave)):
return True
return False
class Solution:
def numberOfLines(self, widths: List[int], S: str) -> List[int]:
cnt = 0
row = 0
for s in S:
cnt += widths[ord(s) - 97]
if cnt == 100:
row += 1
cnt = 0
elif cnt > 100:
row += 1
cnt = widths[ord(s) - 97]
return [row + 1, cnt]
class Solution:
def maxIncreaseKeepingSkyline(self, grid: List[List[int]]) -> int:
n = len(grid)
row = [max(grid[i]) for i in range(n)]
col = [max(grid[i][j] for i in range(n)) for j in range(n)]
res = 0
for i in range(n):
for j in range(n):
res += min(row[i], col[j]) - grid[i][j]
return res
class Solution:
def soupServings(self, N: int) -> float:
if N > 4800: return 1
dic = dict()
def solve(A, B):
if (A, B) in dic:
return dic[(A, B)]
if A <= 0 and B > 0:
res = 1
elif A <= 0 and B <= 0:
res = 0.5
elif A > 0 and B <= 0:
res = 0
else:
res = 0.25 * (solve(A - 100, B) + solve(A - 75, B - 25) + solve(A - 50, B - 50) + solve(A - 25, B - 75))
dic[(A, B)] = res
return res
return solve(N, N)
class Solution:
def expressiveWords(self, S: str, words: List[str]) -> int:
res = 0 # 可扩张的单词数量
counter_S = self.getCounter(S)
n = len(counter_S)
for word in words:
counter_W = self.getCounter(word)
if len(counter_W) != n:
# 如果计数数组长度都不一样,直接判断不能扩张,对下一个单词进行判断
continue
for i in range(n):
if counter_S[i][0] != counter_W[i][0]:
# 如果对应位置字母不相同,则不能扩张,跳出循环
break
if counter_W[i][1] == 1 and (counter_S[i][1] >= 3 or counter_S[i][1] == 1):
# 字母计数为1,对应的位置可以不扩张=1,也可以扩张 >=3
pass
elif counter_W[i][1] == 2 and (counter_S[i][1] >= 3 or counter_S[i][1] == 2):
# 字母计数为2,对应的位置可以不扩张=2,也可以扩张 >=3
pass
elif counter_W[i][1] >= 3 and counter_S[i][1] >= counter_W[i][1]:
# 字母计数超过3的情况,对应的位置,只需满足扩张后的计数比原来多即可
pass
else:
# 剩下的情况是不合法的扩张,直接跳出循环
break
if i == len(counter_S) - 1:
# 如果遍历到最后一个字母,仍符合扩张要求,则单词计数 +1
res += 1
# print(word)
return res
def getCounter(self, S: str) -> list:
counter = []
for w in S:
if counter == []: # 初始情况
counter.append([w, 1])
else:
if w == counter[-1][0]:
# 和前面的字母一样,那么计数+1
counter[-1][1] += 1
else:
# 不一样,就是新的尾字母
counter.append([w, 1])
return counter
# 正则
import re # use re module to process regular expression matching problems
class Solution:
def expressiveWords(self, S: str, words) -> int:
pattern = '^' # '^' match the start of target word
a = [i for i in range(len(S)) if i == 0 or S[i] != S[i-1]] + [len(S)] # record the start indexes of continuous same letter; e.g.'aaabbcc': [0, 3, 5]
for i in range(1, len(a)):
cha, num = S[a[i]-1], a[i] - a[i-1] # cha is the continuous same letters, num is length
print(cha, num)
if num < 3:
pattern += cha * num # if num < 3, it means not stretchy, so the regular expression should be this 'cha' itself
if num >= 3:
pattern += '%s{1,%d}' % (cha, num) # if num > 3, it means any length that not longer than num (but need to exist) could be OK, so the regular expression is '%s{1,%d}'
pattern += '$' # '$' match the end of target word
res = 0 # use res as a count
for word in words:
if re.match(pattern, word):
res += 1 # use regular expression to match word in words one by one
return res
class Solution:
def xorGame(self, nums: List[int]) -> bool:
if len(nums) % 2 == 0:
return True
xor_val = 0
for val in nums:
xor_val ^= val
return xor_val == 0