第二週LeetCode記錄
9.14 7.託普里茨矩陣
如果矩陣上每一條由左上到右下的對角線上的元素都相同,那麼這個矩陣是 託普利茨矩陣 。
給定一個 M x N 的矩陣,當且僅當它是託普利茨矩陣時返回 True。
輸入:
matrix = [
[1,2,3,4],
[5,1,2,3],
[9,5,1,2]
]
輸出: True
解釋:
在上述矩陣中, 其對角線為:
"[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]"。
各條對角線上的所有元素均相同, 因此答案是True。
思路
滿足條件是任一個座標(x,y)的值都等於(x+1,y+1)的值,並且要做好條件限制
我的解:未解出來
最優解
class Solution:
def isToeplitzMatrix(self, matrix: list) -> bool:
res = matrix[0][:-1]
for line in matrix[1:]:
tmp = line[1:]
if res != tmp:
return False
res = line[:-1]
return True
總結
找到行與行之間的聯絡,逐行比較,而不是行內元素比較。行內元素符合的規律,那麼行內元素組成的集合也符合這個規律,作為一個整體。
9.15 8. 重複疊加字串匹配
給定兩個字串 A 和 B, 尋找重複疊加字串A的最小次數,使得字串B成為疊加後的字串A的子串,如果不存在則返回 -1。
舉個例子,A = "abcd",B = "cdabcdab"。
答案為 3, 因為 A 重複疊加三遍後為 “abcdabcdabcd”,此時 B 是其子串;A 重複疊加兩遍後為"abcdabcd",B 並不是其子串。
注意:
A 與 B 字串的長度在1和10000區間範圍內。
思路
每次拼接a進行測試,是否能匹配到b
我的解法:
class Solution: def repeatedStringMatch(self, a: str, b: str) -> int: for i in range(1,10000//len(a)+1): c = a * i if c.find(b) != -1: return i return -1
9.16 9. 基本計算器
實現一個基本的計算器來計算一個簡單的字串表示式的值。
字串表示式可以包含左括號 (
,右括號 )
,加號 +
,減號 -
,非負整數和空格
。
示例 1:
輸入: "1 + 1"
輸出: 2
輸入: "(1+(4+5+2)-3)+(6+8)"
輸出: 23
思路:
無括號情況:如“1+3+22-33+2-6-7” 按加號分割,並且strip會得到單數字和含減號表示式,以及含減號表示式的[1,3,22-33,2-6-7],如果長度大於1則是含減號,用減號分割,求差,再總體求和
有括號情況:去掉所有空格,遍歷,記錄左括號出現的次數(key)和位置(value)和右括號對應的位置贊為none,如果出現了右括號,記錄右括號出現的位置和上一個左括號的位置,並且填充之前為none的對應左括號記錄的右括號的位置,每一組括號先求和,帶入到上一組。
最優解
解決 - 結合律的問題的一個分廠簡單的方法就是使將 - 運算子看作右側運算元的大小。一旦我們將 - 看作運算元的大小,則表示式將只剩下一個操作符。就是 + 運算子,而 + 是遵循結合律的。
例如,A-B-CA−B−C 等於 A + (-B) + (-C)A+(−B)+(−C)。
重寫以後的表示式將遵循結合律,所以我們從左或從右計算表示式都是正確的。
我們需要注意的是給定的表示式會很複雜,即會有巢狀在其他表示式的表示式。即 (A - (B - C)),我們需要 B-C 外面的 - 號與 B-C 關聯起來,而不是僅僅與 B 關聯起來。
/ 我們可以通過遵循前面的基本練習並將符號與其右側的表示式關聯來解決此問題。然而,我們將採用的方法有一個小的轉折,因為我們將在執行中評估大多數表示式。這減少了推送和彈出操作的數量。
演算法:
正序迭代字串。
運算元可以由多個字元組成,字串 "123" 表示數字 123,它可以被構造為:123 >> 120 + 3 >> 100 + 20 + 3。如果我們讀取的字元是一個數字,則我們要將先前形成的運算元乘以 10 並於讀取的數字相加,形成運算元。
每當我們遇到 + 或 - 運算子時,我們首先將表示式求值到左邊,然後將正負符號儲存到下一次求值。
如果字元是左括號 (,我們將迄今為止計算的結果和符號新增到棧上,然後重新開始進行計算,就像計算一個新的表示式一樣。
如果字元是右括號 ),則首先計算左側的表示式。則產生的結果就是剛剛結束的子表示式的結果。如果棧頂部有符號,則將此結果與符號相乘。
PythonJava
class Solution:
def calculate(self, s: str) -> int:
stack = []
operand = 0
res = 0 # For the on-going result
sign = 1 # 1 means positive, -1 means negative
for ch in s:
if ch.isdigit():
# Forming operand, since it could be more than one digit
operand = (operand * 10) + int(ch)
elif ch == '+':
# Evaluate the expression to the left,
# with result, sign, operand
res += sign * operand
# Save the recently encountered '+' sign
sign = 1
# Reset operand
operand = 0
elif ch == '-':
res += sign * operand
sign = -1
operand = 0
elif ch == '(':
# Push the result and sign on to the stack, for later
# We push the result first, then sign
stack.append(res)
stack.append(sign)
# Reset operand and result, as if new evaluation begins for the new sub-expression
sign = 1
res = 0
elif ch == ')':
# Evaluate the expression to the left
# with result, sign and operand
res += sign * operand
# ')' marks end of expression within a set of parenthesis
# Its result is multiplied with sign on top of stack
# as stack.pop() is the sign before the parenthesis
res *= stack.pop() # stack pop 1, sign
# Then add to the next operand on the top.
# as stack.pop() is the result calculated before this parenthesis
# (operand on stack) + (sign on stack * (result from parenthesis))
res += stack.pop() # stack pop 2, operand
# Reset the operand
operand = 0
return res + sign * operand
總結:
最優解遍歷了字串。
如果是數字接著遍歷,*10 + 遍歷的數,
如果是+就累加,-就改變符號為負一,繼續累加後面的。
如果是(,先把之前的結果和符號,放進stack,還原符號和累加結果,重新算裡面的。
如果是),先得出括號裡的結果,在乘以pop出來的一個符號。
遇到括號就是要算出結果,放入stack中,連同符號push一起算出來。
9.17 10.表示數值的字串
請實現一個函式用來判斷字串是否表示數值(包括整數和小數)。例如,字串"+100"、"5e2"、"-123"、"3.1416"、"-1E-16"、"0123"都表示數值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。
思路
總結是數字的幾種情況
我的解
class Solution:
def isNumber(self, s: str) -> bool:
from collections import Counter
li = list(s.strip())
if len(li) == 0:
return False
# 長度為1的情況
if len(li) == 1:
if "0123456789".find(li[0]) == -1:
return False
# 如果以符號開頭
if li[0] in ['+','-']:
# 判斷後面有沒有E
if 'e' in li:
index = li.index('e')
new_li = li[index:]
# 加減號之一出現1次,或數字
if new_li[1] not in ['+','-']:
return False
res = sum(not i.isdigit() for i in new_li[2:])
if res != 0:
return False
new_li_2 = li[1:index]
# e前多於兩個小數點
if Counter(new_li_2).get('.') > 1:
return False
res2 =sum(not i.isdigit() for i in new_li_2)
if res2 != 0:
return False
elif 'E' in li:
index = li.index('E')
new_li = li[index:]
# 加減號之一出現1次,或數字
if new_li[1] not in ['+','-']:
return False
res = sum(i.isdigit() for i in new_li[2:])
if res != 0:
return False
new_li_2 = li[1:index]
# e前多於兩個小數點
if Counter(new_li_2).get('.') > 1:
return False
res2 =sum(not i.isdigit() for i in new_li_2)
if res2 != 0:
return False
# 第一個不是數字且不是小數點
elif not li[0].isdigit() and li[0]!='.':
return False
# 沒有符號
else:
if 'e' in li:
index = li.index('e')
new_li = li[index:]
# 加減號之一出現1次,或數字
if(len(new_li)==1):
return False
if new_li[1] not in ['+','-']:
return False
res = sum(not i.isdigit() for i in new_li[2:])
if res != 0:
return False
new_li_2 = li[1:index]
# e前多於兩個小數點
if Counter(new_li_2).get('.') > 1:
return False
res2 =sum(not i.isdigit() for i in new_li_2)
if res2 != 0:
return False
elif 'E' in li:
index = li.index('E')
new_li = li[index:]
# 加減號之一出現1次,或數字
if new_li[1] not in ['+','-']:
return False
res = sum(i.isdigit() for i in new_li[2:])
if res != 0:
return False
new_li_2 = li[1:index]
# e前多於兩個小數點
if Counter(new_li_2).get('.') > 1:
return False
res2 =sum(not i.isdigit() for i in new_li_2)
if res2 != 0:
return False
else:
if "." in Counter(li):
if Counter(li).get('.') > 1:
return False
elif Counter(li).get('.') == 1:
res2 =sum(not i.isdigit() for i in li)
if res2 > 1:
return False
else:
res3 =sum(not i.isdigit() for i in li)
if res3 > 0:
return False
return True
最優解:
class Solution:
def isNumber(self, s: str) -> bool:
states = [
{ ' ': 0, 's': 1, 'd': 2, '.': 4 }, # 0. start with 'blank'
{ 'd': 2, '.': 4 } , # 1. 'sign' before 'e'
{ 'd': 2, '.': 3, 'e': 5, ' ': 8 }, # 2. 'digit' before 'dot'
{ 'd': 3, 'e': 5, ' ': 8 }, # 3. 'digit' after 'dot'
{ 'd': 3 }, # 4. 'digit' after 'dot' (‘blank’ before 'dot')
{ 's': 6, 'd': 7 }, # 5. 'e'
{ 'd': 7 }, # 6. 'sign' after 'e'
{ 'd': 7, ' ': 8 }, # 7. 'digit' after 'e'
{ ' ': 8 } # 8. end with 'blank'
]
p = 0 # start with state 0
for c in s:
if '0' <= c <= '9': t = 'd' # digit
elif c in "+-": t = 's' # sign
elif c in "eE": t = 'e' # e or E
elif c in ". ": t = c # dot, blank
else: t = '?' # unknown
if t not in states[p]: return False
p = states[p][t]
return p in (2, 3, 7, 8)
總結:
此題涉及到自動機,要複習一下自動機的演算法原理是什麼。做下標記,後期在看。
9.18 11. 把陣列排成最小的數
輸入一個非負整數陣列,把數組裡所有數字拼接起來排成一個數,列印能拼接出的所有數字中最小的一個。
輸入: [10,2]
輸出: "102"
思路
先求出陣列中 位數最多是幾位,所有元素補0,排序。最小的數往前排,大的數還原往後排
我的解
class Solution:
@classmethod
def minNumber(self, nums: list) -> str:
new_li = sorted(nums)
print(new_li)
max_len = len(str(new_li[-1]))
dic = dict()
index = 0
for i in new_li:
dic[index] = int(str(i).ljust(max_len,'0'))
print(dic)
index += 1
new_dic = sorted(dic.items(),key=lambda v:v[1])
print(new_dic)
res = ""
for item in new_dic:
res = res + str(new_li[item[0]])
return res
錯誤總結
沒有考慮到 3,30這種形式的排序
最優解
- 快速排序
class Solution:
def minNumber(self, nums: List[int]) -> str:
def fast_sort(l , r):
if l >= r: return
i, j = l, r
while i < j:
while strs[j] + strs[l] >= strs[l] + strs[j] and i < j: j -= 1
while strs[i] + strs[l] <= strs[l] + strs[i] and i < j: i += 1
strs[i], strs[j] = strs[j], strs[i]
strs[i], strs[l] = strs[l], strs[i]
fast_sort(l, i - 1)
fast_sort(i + 1, r)
strs = [str(num) for num in nums]
fast_sort(0, len(strs) - 1)
return ''.join(strs)
- 內建函式
class Solution:
def minNumber(self, nums: List[int]) -> str:
def sort_rule(x, y):
a, b = x + y, y + x
if a > b: return 1
elif a < b: return -1
else: return 0
strs = [str(num) for num in nums]
strs.sort(key = functools.cmp_to_key(sort_rule))
return ''.join(strs)
總結
制定一個排序規則,比如3,30,如果3,30>303則3>30,按照這個規則快速排序成一個新的陣列即可。
9.19 12. 統計作戰單位數
n 名士兵站成一排。每個士兵都有一個 獨一無二 的評分 rating 。
每 3 個士兵可以組成一個作戰單位,分組規則如下:
從隊伍中選出下標分別為 i、j、k 的 3 名士兵,他們的評分分別為 rating[i]、rating[j]、rating[k] 作戰單位需滿足: rating[i] < rating[j] < rating[k] 或者 rating[i] > rating[j] > rating[k] ,其中 0 <= i < j < k < n 請你返回按上述條件可以組建的作戰單位數量。每個士兵都可以是多個作戰單位的一部分。
示例 1:
輸入:rating = [2,5,3,4,1]
輸出:3
解釋:我們可以組建三個作戰單位 (2,3,4)、(5,4,1)、(5,3,1) 。
示例 2:
輸入:rating = [2,1,3]
輸出:0
解釋:根據題目條件,我們無法組建作戰單位。
示例 3:
輸入:rating = [1,2,3,4]
輸出:4
思路
列表長度至少大於等於3,3層遍歷。
我的解
class Solution:
@classmethod
def numTeams(self, rating: list) -> int:
if len(rating) < 3:
return 0
res = 0
for i in range(len(rating)):
if i >= len(rating) -2: break
for j in range(i+1,len(rating)):
if j >= len(rating) -1: break
for k in range(j+1,len(rating)):
new_li = []
new_li.append(rating[i])
new_li.append(rating[j])
new_li.append(rating[k])
if all(x<y for x,y in zip(new_li,new_li[1:])) or all(x>y for x,y in zip(new_li,new_li[1:])):
res += 1
return res
最優解
class Solution:
def numTeams(self, rating: List[int]) -> int:
def func(nums):
dp = [0] * len_
res = 0
for i in range(1, len_):
idx = i - 1
while idx >= 0:
if nums[i] > nums[idx]:
dp[i] += 1
if dp[idx] > 0:
res += dp[idx]
idx -= 1
return res
len_ = len(rating)
return func(rating[::-1]) + func(rating)
總結
- 更新dp的同時,更新res;
- dp[i]記錄的是第i個數之前比其值小的數的個數;
- 兩層判斷,如果nums[i] > nums[idx], 更新dp[i],其次,如果dp[idx]>0則再更新res。因為此時,num[i]已經大於nums[idx],再算上一個比nums[idx]小的數,就構成了一個3個數的升序,這樣的組合有dp[idx];
- 另外的一種情況,將陣列逆序即可。