我的解法:
class Solution:
def trailingZeroes(self, n: int) -> int:
fives = 0
while n > 0:
n = n//5
fives += n
return fives
末尾的一个零表示阶乘中至少有一对2和5的因子,由于2因子的数量远多于5因子,因此只需考虑阶乘的所有乘数中一共有多少个5因子。需要注意的是有的乘数会包含多个5因子,如25包含两个5因子。因此,我们需要可以通过n/5来统计含有一个5因子的乘数数量,n//25统计含有两个5因子的乘数数量,以此类推,最终得到所有5因子的数量也就是阶乘后0的数量。
我的解法:
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
carry = 0
dummy_node = ListNode()
curr = dummy_node
while l1 or l2 or (carry > 0):
if (not l1) and l2:
sumi = l2.val+carry
elif (not l2) and l1:
sumi = l1.val+carry
elif (not l1) and (not l2):
sumi = carry
else:
sumi = l1.val+l2.val+carry
carry, rem = sumi//10, sumi%10
curr.next = ListNode(rem)
curr = curr.next
if l1:
l1 = l1.next
if l2:
l2 = l2.next
return dummy_node.next
同时遍历两链表,记录对应元素的和的进位值和余数。若其中一个链表已经为空,则计算另一链表与进位值之和,更新进位值和余数;若两链表均为空,但进位值仍然大于0,则只考虑进位值,再运行一次创建新节点。开始时创建伪头节点,将各新增节点依次链接到尾部。时间复杂度O(n),空间复杂度O(n)。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
hist, t, t_max, i = [], 0, 0, 0
while i < len(s):
if s[i] not in hist:
hist.append(s[i])
t += 1
i += 1
else:
hist = hist[hist.index(s[i])+1:]
t_max = max([t_max, t])
t = len(hist)
return max([t_max, t])
创建队列,遍历字符串,若当前字符不在队列中,则压入队列;若之前存在于队列中,则记录当前最大连续值,同时将之前出现过的当前字符及其之前字符从队列中推出,保证队列长度即为有效连续无重复字符串。时间复杂度O(n2)。
大佬解法:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
lp, rp, max_, hist= 0, 0, 0, []
while rp < len(s):
if s[rp] not in hist:
hist.append(s[rp])
rp += 1
else:
max_ = max([max_, rp-lp])
lp += 1
hist = hist[1:]
return max([max_, rp-lp])
定义左指针指向开始的元素,从第一个元素开始,右指针向后遍历寻找最长无重复子窜,用列表hist记录出现过的所有字符,记录当前最长子串数。左指针右移一格,删除hist中的第一个元素,右指针继续向后遍历,重复操作至右指针指向字符串尾部。时间复杂度O(n)。
大佬解法:
class Solution:
def longestPalindrome(self, s: str) -> str:
n, ans= len(s), ""
dp = [[False]*n for _ in range(n)]
for l in range(n):
for i in range(n):
j = i + l
if j == n:
break
if l == 0:
dp[i][j] = True
elif l == 1:
dp[i][j] = s[i] == s[j]
else:
dp[i][j] = dp[i+1][j-1] and s[i] == s[j]
if dp[i][j] and l+1>len(ans):
ans = s[i:i+l+1]
return ans
动态规划。定义状态dp[i][j]为字符串s[i:j]是否为回文串。定义l为字符子串的长度,即j=i+l。动态规划边界条件为,dp[i][i]为True;当l为1,即子字符长度为2时,dp[i][j]为两字符是否相等。其他情况下,状态转移条件为,若子字符串两端字符相等,且内部字符串为回文串,则该子字符串也为回文串,即dp[i+1][j-1] == True and s[i] == s[j]。遍历i和l后即可找到相应最长回文子串。时间复杂度O(n2)。
我的解法:
class Solution:
def convert(self, s: str, numRows: int) -> str:
if not s:
return ""
if numRows == 1:
return s
n = len(s)
main, res, add, res2 = n//(2*numRows-2), n%(2*numRows-2), 0, 0
mains = [n*(numRows-1) for n in range(main)]
if res > 0:
add = 1
if res//numRows >=1:
mains.append(main*2)
res2 = res%numRows
matrix = [[""]*(main*(numRows-1)+add+res2) for _ in range(numRows)]
i, row, col = 0, 0, 0
while 1:
while (i < n) and (row < numRows):
matrix[row][col] = s[i]
i += 1
row += 1
row -= 1
while (i < n) and (row > 1):
row -= 1
col += 1
matrix[row][col] = s[i]
i += 1
row -= 1
col += 1
if i == n:
break
ans = ''
for r in range(numRows):
ans += "".join(matrix[r])
return ans
暴力解法,生成目标z字形结构后,将结果逐一读取得到结果。
大佬解法:
class Solution:
def convert(self, s: str, numRows: int) -> str:
n = len(s)
if numRows <= 1:
return s
rows = [""]*numRows
i, r, flag = 0, 0, 1
while i < n:
rows[r] += s[i]
r += flag
i += 1
if r >= numRows-1:
flag = -1
if r == 0:
flag = 1
return "".join(rows)
并不需要真的做出一个题目描述的Z字形数组,关键在于找到字符串各个字母位于的行数。我们发现行数变化的规律为,当触及底部或顶部时,行数变化方向会改变,我们用flag来记录移动方向,1为向下,-1为向上。我们将位于不同行的字符按行数先后组合后即为最终答案。
class Solution:
def myAtoi(self, str_: str) -> int:
str_ = str_.lstrip()
if not str_:
return 0
sign, int_, i = 1, 0, 0
if str_[0] == '-':
sign = -1
str_ = str_[1:]
elif str_[0] == '+':
str_ = str_[1:]
if not str_ or not str_[0].isdigit():
return 0
while i < len(str_):
if str_[i].isdigit():
i += 1
else:
break
int_ = int(str_[:i])*sign
if int_ > 2**31-1:
return 2**31-1
elif int_ < -2**31:
return -2**31
else:
return int_
直接考虑各类特殊情况条件判断,得到最终结果。
大佬解法:
class Solution:
def __init__(self):
self.transfer = {
'start':['start', 'signed', 'number', 'end'], 'signed':['end', 'end', 'number', 'end'],
'number':['end', 'end', 'number', 'end']}
self.start_status = 'start'
# ' ', '+/-', 'number', 'other'
def myAtoi(self, str_: str) -> int:
c, sign, ans = self.start_status, 1, 0
for s in str_:
if s == ' ':
c = self.transfer[c][0]
elif s == '+':
c = self.transfer[c][1]
elif s == '-':
c = self.transfer[c][1]
if c == 'signed':
sign = -1
elif s.isdigit():
c = self.transfer[c][2]
ans = ans*10 + int(s)
else:
c = self.transfer[c][3]
if c == 'end':
break
return min([2**31-1,max([ans*sign, -2**31])])
使用自动机方法,定义四个状态以及遇到不同字符后状态的变化方式,如上图所示。遍历字符串,设置初始状态为start,根据遍历到的字符变换状态,同时更新结果,直到状态变为end时,输出结果。
大佬解法:
class Solution:
def maxArea(self, height: List[int]) -> int:
def volume(lp, rp):
return (rp-lp) * min([height[rp],height[lp]])
lp, rp, max_ = 0, len(height)-1, 0
while rp > lp:
max_ = max([volume(lp,rp), max_])
if height[lp] >= height[rp]:
rp -= 1
else:
lp += 1
return max_
双指针法,开始时左指针指向列表头部,右指针指向列表尾部,记录当前纳水量。将高度较小的指针向中间方向移动,记录最大纳水量,直到两指针相会。移动高度较小指针是因为若移动高度较大指针,纳水量一定小于不移动时纳水量,移动高度较小指针能考虑其他可能结果。时间复杂度为O(n)。
class Solution:
def intToRoman(self, num: int) -> str:
roms = ['I', 'V', 'X', 'L', 'C', 'D', 'M']
nums = [1, 5, 10, 50, 100, 500, 1000]
ans = ''
while num > 0:
for i in range(6, -1, -2):
ali = num//nums[i]
rem = num%nums[i]
if (ali < 4) & (ali >= 1):
ans += roms[i]*ali
num = rem
elif ali == 4:
ans += (roms[i]+roms[i+1])
num = rem
elif (ali > 4) & (ali < 9):
ans += (roms[i+1]+roms[i]*(ali-5))
num = rem
elif ali == 9:
ans += (roms[i]+roms[i+2])
num = rem
else:
continue
return ans