前言
繼續leetcode刷題生涯
這裏記錄的都是筆者覺得有點意思的做法
參考了好幾位大佬的題解,尤其是powcai大佬和labuladong大佬,感謝各位大佬
412. Fizz Buzz
class Solution:
def fizzBuzz(self, n: int) -> List[str]:
res = []
for i in range(1, n+1):
if i % 3 == 0 and i % 5 == 0:
res.append("FizzBuzz")
elif i % 3 == 0:
res.append("Fizz")
elif i % 5 == 0:
res.append("Buzz")
else:
res.append(str(i))
return res
413. 等差數列劃分
# 滑動窗口
class Solution:
def numberOfArithmeticSlices(self, A: List[int]) -> int:
if len(A) < 3: return 0
left = 0
right = 2
res = 0
while right < len(A):
if A[right] - A[right-1] == A[left+1] - A[left]:
res += right - left -1
right += 1
else:
left = right -1
right = left +2
return res
# dp
class Solution:
def numberOfArithmeticSlices(self, A: List[int]) -> int:
# 至少3個才叫數列,纔有等差這種概念
# f[i]表示到i元素爲止的等差數列個數,差(d)是一樣的
# A[i] - A[i-1]等於A[i-1] - A[i-2],則f[i] = f[i-1] + 1
# 從左往右更新
n = len(A)
if n < 3: return 0
f = [0] * n
# f[0], f[1], f[2]
for i in range(2, n):
if A[i] - A[i - 1] == A[i - 1] - A[i - 2]:
f[i] = f[i - 1] + 1
return sum(f)
414. 第三大的數
# 暴力
class Solution:
def thirdMax(self, nums: List[int]) -> int:
nums = sorted(set(nums))
if len(nums) < 3: return nums[-1]
return nums[-3]
# 三個數
class Solution:
def thirdMax(self, nums: List[int]) -> int:
first = second = third = float('-inf')
for num in nums:
if num > third: # 通過第3關
if num < second:
third = num
elif num > second: # 通過第2關
if num < first:
third = second
second = num
elif num > first: # 通過第1關
third = second
second = first
first = num
if third == float('-inf'):
return first
else:
return third
415. 字符串相加
class Solution:
def addStrings(self, num1: str, num2: str) -> str:
res = ""
i, j, carry = len(num1) - 1, len(num2) - 1, 0
while i >= 0 or j >= 0:
n1 = int(num1[i]) if i >= 0 else 0
n2 = int(num2[j]) if j >= 0 else 0
tmp = n1 + n2 + carry
carry = tmp // 10
res = str(tmp % 10) + res
i, j = i - 1, j - 1
return "1" + res if carry else res
416. 分割等和子集
# 暴力
class Solution:
def canPartition(self, nums: List[int]) -> bool:
n = len(nums)
target = sum(nums)
if target % 2: return False
target //= 2
dic = set() #用於儲存當前元素和的可能情況
dic.add(0)
for i in range(n):
dic_tmp = set() #用於存儲經過這一步操作,增加的元素和狀態
for j in dic:
tmp = j + nums[i]
if tmp == target:
return True
if tmp < target:
dic_tmp.add(tmp)
for j in dic_tmp:
dic.add(j)
return False
# 揹包問題
class Solution:
def canPartition(self, nums: List[int]) -> bool:
avg, mod = divmod(sum(nums), 2)
# 不能被2整除
if mod != 0: return False
n = len(nums)
dp = [[1] + [0] * avg for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, avg + 1):
if j - nums[i - 1] >= 0:
dp[i][j] = dp[i - 1][j - nums[i - 1]] | dp[i - 1][j]
return dp[-1][-1]
417. 太平洋大西洋水流問題
# 從邊緣開始的dfs
class Solution:
def pacificAtlantic(self, matrix: List[List[int]]) -> List[List[int]]:
if not matrix or not matrix[0]: return []
res1 = set() #流向太平洋的位置
res2 = set() #流向大西洋的位置
row = len(matrix)
col = len(matrix[0])
# 從邊界開始的dfs
def dfs(i, j, cur, res):
res.add((i, j))
for x, y in [[1, 0], [-1, 0], [0, 1], [0, -1]]:
tmp_i = i + x
tmp_j = j + y
if 0 <= tmp_i < row and 0 <= tmp_j < col and matrix[i][j] <= matrix[tmp_i][tmp_j] and (tmp_i, tmp_j) not in res:
dfs(tmp_i, tmp_j, matrix[i][j], res)
# 太平洋
for i in range(row):
dfs(i, 0, 0, res1)
# 太平洋
for j in range(col):
dfs(0, j, 0, res1)
# 大西洋
for i in range(row):
dfs(i, col - 1, 0, res2)
# 大西洋
for j in range(col):
dfs(row - 1, j, 0, res2)
return res1 & res2
419. 甲板上的戰艦
# 甲板上哪來的戰艦。。。
class Solution:
def countBattleships(self, board: List[List[str]]) -> int:
row = len(board)
col = len(board[0])
res = 0
for i in range(row):
for j in range(col):
if board[i][j] == ".":
continue
if i > 0 and board[i - 1][j] == "X":
continue
if j > 0 and board[i][j - 1] == "X":
continue
res += 1
return res
420. 強密碼檢驗器
class Solution:
def strongPasswordChecker(self, s: str) -> int:
has_lower, has_upper, has_num = 0, 0, 0
for ch in s:
if ch.isnumeric(): has_num = 1
elif ch.isupper(): has_upper = 1
elif ch.islower(): has_lower = 1
if len(s) < 6:
# 總長度不到6時候,肯定至少補一次字符,補自字符同時就可以
# 把連續的3個字符打斷,最長只可能5個連續字符,插入一次字符
# 就可以完成分割,所以只需要關注字符種類夠不夠,假設缺少Ncat
# 種字符,那就需要添加max(len(s) - 6, Ncat)個字符滿足要求
return max(3 - has_num - has_upper - has_lower, 6 - len(s))
elif len(s) <= 20:
'''
這種情況不需要考慮長度問題,長度已經符合條件了,對於破壞三個連續字符這個操作而言,修改
字符是開銷最小的,連續n個字符連續,只需要每3個把最後一個字符修改成不一樣的字符即可,總共
修改n//3次,而且還不會影響字符串長度,對於字符類型不足的問題,也可以通過修改字符來滿足條件
所以修改字符這個操作可以同時對3字符連續和字符種類不足兩種錯誤進行修正,且不會造成字符串
長度問題,假設連續字符問題總共需要修改Nseq次滿足要求,字符種類不足問題總共需要修改Ncat次
才能滿足要求,那最佳修改方案就是max(Nseq, Ncat)次修改滿足題目題目要求
'''
i = 0
Nseq = 0
while i < len(s):
j = i
while j < len(s) and s[j] == s[i]:
j += 1
if j - i >= 3:
Nseq += (j - i) // 3
i = j
return max(3 - has_num - has_upper - has_lower, Nseq)
else:
'''
對於字符串長度大於20的情況,最少會有len(s) - 20 次刪除的操作,這些刪除操作是節約不掉的
刪除不可能解決字符類型不足的問題但是可能解決3個連續字符的問題,所以刪除時候儘量讓3個連續
字符問題多得到解決,這樣在字符串長度減少到20個時候,繼續解決3個連續字符的問題和字符種類
不足的問題時候,剩下操作次數就最少
考慮n個連續的字符,n%3 == 0 的情況下, 只要刪掉尾巴上的字符,就可以解決一個3字符連續問題,
轉變成n%3 == 2的狀態,
n%3 == 1 的情況,需要刪除2個字符才能減少一個3字符連續問題,轉變成n%3 == 0狀態
n%3 == 2時候,需要刪除3個字符才能減少一個三字符連續問題,轉變成n%3 == 1狀態
所以對於連續3字符問題,需要按照這個優先級來進行處理,直到剩餘字符爲20個
刪除字符時候一定能找到不會讓字符種類數減少的方式進行刪除,所以不需要考慮字符類型不足的問題
等字符數量減到20個的時候,再通過修改字符的方式來和3字符連續的問題一起解決
'''
from queue import PriorityQueue
que = PriorityQueue()
i = 0
while i < len(s):
j = i
while j < len(s) and s[j] == s[i]:
j += 1
if j - i >= 3:
que.put(((j - i) % 3, j - i))
i = j
del_cnt = len(s) - 20
for _ in range(del_cnt):
if que.empty():
break
mod, val = que.get()
if val > 3:
if mod == 0:
que.put((2, val - 1))
elif mod == 1:
que.put((0, val - 1))
else:
que.put((1, val - 1))
# 字符剩餘20個時候,繼續解決3字符連續問題和種類不足問題
Nseq = 0
while not que.empty():
_, val = que.get()
Nseq += val // 3
return del_cnt + max(3 - has_upper - has_lower - has_num, Nseq)