文章目录
- 1.斐波那契数列
- 2.跳台阶
- 3.最小花费爬楼梯
- 4.最长公共子序列(二)
- 5.最长公共子串
- 6.不同路径的数目(一)
- 7.矩阵的最小路径和
- 8.把数字翻译成字符串
- 9.兑换零钱(一)
- 10.兑换零钱(二)完全背包问题 需要输出有多少种情况
- 11.最长上升子序列(一)(可以删除子序列的一些数 12645→1245)(只用输出最长长度)
- 12.连续子数组的最大和
- 13.最长回文子串
- 14.数字字符串转化成IP地址
- 15.编辑距离(一)
- 16.编辑距离(二)
- 17.正则表达式匹配(内部有特殊符号代表任意字符或者任意次数)
- 18.最长的括号子串
- 19.打家劫舍(一)有时候需要连续放弃两家不偷
- 20.打家劫舍(二)第一家和最后一家是相邻的
- 21.买卖股票的最好时机(一)只买卖各一次
- 22.买卖股票的最好时机(二)可以多次买卖
- 23.买卖股票的最好时机(三)可以买卖各两次 但必须卖空才能买
1.斐波那契数列
数据范围:1≤n≤40
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n) ,本题也有时间复杂度 O(logn)O(logn) 的解法
输入描述:
一个正整数n
返回值描述:
输出一个正整数。
示例1
输入:4
返回值:3
说明:根据斐波那契数列的定义可知,fib(1)=1,fib(2)=1,fib(3)=fib(3-1)+fib(3-2)=2,fib(4)=fib(4-1)+fib(4-2)=3,所以答案为3。
输入:1
返回值:1
示例3
输入:2
返回值:1
class Solution:
def Fibonacci(self , n: int) -> int:
# write code here
a,b=1,1
if n<=2:
return 1
else:
for i in range(3,n+1):
a,b=b,a+b
return b
# return self.Fibonacci(n-1)+self.Fibonacci(n-2)# 直接递归时间会爆炸
2.跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
数据范围:1≤n≤40
要求:时间复杂度:O(n)O(n) ,空间复杂度: O(1)O(1)
示例1
输入:2
返回值:2
说明:青蛙要跳上两级台阶有两种跳法,分别是:先跳一级,再跳一级或者直接跳两级。因此答案为2
示例2
输入:7
返回值:21
class Solution:
def jumpFloor(self , number: int) -> int:
# write code here
a,b=1,2
if number<3:return number
for i in range(number-2):
a,b=b,a+b
return b
# # 这个的最大深度太大了
# if number==1:return 1
# if number==2:return 2
# return self.jumpFloor(number-1)+self.jumpFloor(number-2)
3.最小花费爬楼梯
输入:[2,5,20]
返回值:5
说明:你将从下标为1的台阶开始,支付5 ,向上爬两个台阶,到达楼梯顶部。总花费为5
输入:[1,100,1,1,1,90,1,1,80,1]
返回值:6
说明:
你将从下标为 0 的台阶开始。
1.支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
2.支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
3.支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
4.支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
5.支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
6.支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。
class Solution:
def minCostClimbingStairs(self , cost: List[int]) -> int:
# write code here
# # 从后面几个开始 这个逻辑不太行
# count=len(cost)-3
# p=cost[count+2]
# q=cost[count+1]
# if len(cost)==3:#等于3 就是比较0 1位置的数谁小就行
# return q if q<p+cost[0] else p+cost[0]
# while count>1:
# res=cost[count]+p if p<q else cost[count]+q
# # 这里是在判断是0 还是 1
# p=q
# q=res
# count=count-1
# res1=cost[1]+p if p<q else cost[1]+q
# res0=cost[0]+q
# res=res1 if res1<res0 else res0
# return res
# 没看懂为啥
if len(cost) <3:
return min(cost)
n = len(cost)
a, b = cost[-1], cost[-2]
index = n - 3
while index >= 0:
total = cost[index] +min(a,b)# b一直都是total
a = b
b = total
index -= 1
return min(a, b)
4.最长公共子序列(二)
输入:“1A2C3D4B56”,“B1D23A456A”
返回值:“123456”
输入:“abc”,“def”
返回值:“-1”
输入:“abc”,“abc”
返回值:“abc”
输入:“ab”,“”
返回值:“-1”
class Solution:
def LCS(self , s1: str, s2: str) -> str:
# write code here
if s1 == None or s2 == None:
return '-1'
m, n = len(s1), len(s2)
last = [''] * (n + 1)
for i in range(m):
cur = [''] * (n + 1)
for j in range(n):
if s1[i] == s2[j]:
cur[j + 1] = last[j] + s1[i]
else:
cur[j + 1] = cur[j] if(len(cur[j]) >= len(last[j + 1])) else last[j + 1]
last[:] = cur
return last[-1] if len(last[-1]) > 0 else '-1'
# 这个比较简单,因为他只看最大的子序列,不用看长度啥的,出现过,就往下找下一个就行了
5.最长公共子串
这道题和之前那个不一样,这道题需要挨着
# 这个方法太牛了,直接把小的放进去挨个检查
# 但是有个比较严重的问题就是他忽略了后半段开始一样 就是第一个字符不一样的情况
class Solution:
def LCS(self , str1: str, str2: str) -> str:
# write code here
if len(str1) < len(str2):
str1, str2 = str2, str1
res = ''
max_len = 0
for i in range(len(str1)):
if str1[i - max_len : i+1] in str2:
res = str1[i - max_len : i+1]
max_len += 1
return res
class Solution:
def LCS(self , str1: str, str2: str) -> str:
# write code here
m=[[0 for i in range(len(str2)+1)] for j in range(len(str1)+1)] #生成0矩阵,为方便后续计算,比字符串长度多了一列
mmax=0 #最长匹配的长度
p=0 #最长匹配对应在s1中的最后一位
for i in range(len(str1)):
for j in range(len(str2)):
if str1[i]==str2[j]:
m[i+1][j+1]=m[i][j]+1
if m[i+1][j+1]>mmax:
mmax=m[i+1][j+1]
p=i+1 #p在找尾
return str1[p-mmax:p] #返回最长子串
# python 时间过不去
6.不同路径的数目(一)
class Solution:
def uniquePaths(self , m: int, n: int) -> int:
# write code here
dp = [[0]*n for _ in range(m)]
for i in range(m):
for j in range(n):
if i == 0 or j == 0:
dp[i][j] = 1
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
7.矩阵的最小路径和
class Solution:
def minPathSum(self , matrix: List[List[int]]) -> int:
# write code here
rows = len(matrix)
cols = len(matrix[0])
dp = [[0]*cols for _ in range(rows)]
for i in range(rows):
for j in range(cols):
# 这几个if就是在避免边界出问题
if i == 0 and j == 0:
dp[i][j] = matrix[i][j]
elif i == 0:
dp[i][j] = dp[i][j-1] + matrix[i][j]
elif j == 0:
dp[i][j] = dp[i-1][j] + matrix[i][j]
else:
dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + matrix[i][j]
return dp[-1][-1]
8.把数字翻译成字符串
第四个方法好像牛一点
class Solution:
def solve(self , nums: str) -> int:
if len(nums) == 0 or nums == '0':
return 0
a = b = 1
for i in range(1,len(nums)):
tmp = a
if nums[i] == '0':
if nums[i-1] == '0':
return 0
else:
if nums[i-1] == '1' or (nums[i-1] == '2' and nums[i] <= '6'):
a = a + b
b = tmp
return a
class Solution:
def solve(self , nums: str) -> int:
# 这是一个动态规划
# 这里是写的leetcode的方法 0可以化为a
nums=int(nums)
y=nums%10
a=b=1
while nums:
nums //=10
x=nums%10
a,b=(a+b if 9< x*10+y < 26 else a),a
y=x
return a
class Solution:
def solve(self , nums: str) -> int:
nums=int(nums)
if nums==0:return 0
y=nums%10
a=b=1
while nums:
if nums%10==0 and b >0:
b-=1
a-=1
nums //=10
x=nums%10
a,b=(a+b if 9< x*10+y < 26 else a),a
y=x
return a
class Solution:
def solve(self , nums: str) -> int:
if len(nums) == 0 or nums == '0':
return 0
a = b = 1
for i in range(1,len(nums)):
tmp = a
if nums[i] == '0':
if nums[i-1] == '0' or nums[i-1] >= '3':
return 0
else:
if nums[i-1] == '1' or (nums[i-1] == '2' and nums[i] <= '6'):
a = a + b
b = tmp
return a
9.兑换零钱(一)
class Solution:
def minMoney(self , arr: List[int], aim: int) -> int:
# write code here
if aim==0:return 0
if len(arr)==0 or aim<min(arr):return -1
dp=[float('inf')]*(aim+1)
dp[0]=0
for i in range(aim+1):#背包容量1+1+1这么上去
for j in range(len(arr)):
if i>=arr[j]:
dp[i]=min(dp[i],dp[i-arr[j]]+1)
return dp[aim] if dp[aim] !=float('inf') else -1
class Solution:
def minMoney(self , arr: List[int], aim: int) -> int:
# write code here
# 背包问题需要注意的就是 他是逐一判断的
# 比如说这个地方 他是在 unit = target - i(i in arr) 用现在的i来减
if aim==0:return 0
if len(arr)==0 or aim<min(arr):return -1
mylist_pop = {aim}
mylist_add = set()
res = 1
while mylist_pop or mylist_add:
while mylist_pop:
target = mylist_pop.pop()
for i in arr:
unit = target - i
if unit > 0:
if unit not in myset:
myset.add(unit)
mylist_add.add(unit)
elif unit == 0:
return res
else:
res += 1
mylist_pop,mylist_add = mylist_add,mylist_pop
else:
return -1
10.兑换零钱(二)完全背包问题 需要输出有多少种情况
class Solution:
def change(self , target: int, nums: List[int]) -> int:
# write code here
dp=[0]*(target+1)
dp[0]=1
for i in range(len(nums)):
coin=nums[i]
for j in range(coin,target+1):
dp[j]=dp[j-coin]+dp[j]
return dp[-1]# dp[target]
11.最长上升子序列(一)(可以删除子序列的一些数 12645→1245)(只用输出最长长度)
class Solution:
def LIS(self , arr: List[int]) -> int:
# write code here
if not arr: return 0
n = len(arr)
dp = [1]*n
for i in range(n-1):
for j in range(i+1,n):
if arr[i]<arr[j]:
dp[j] = max(dp[i]+1,dp[j])
return max(dp)
12.连续子数组的最大和
def FindGreatestSumOfSubArray(self , array: List[int]) -> int:
# write code here
# 子数组是在这里边找一段 这里用动态规划 不断的计算前i个的和 最后就可以s[a]+/- s[b]
curSum=0
res=array[0]
for num in array:
if curSum>0:
curSum+=num
else:
curSum=num
res=max(res,curSum)# res存的是当前找到的最大值 curSum是存了在当前位置可以获得的最大值
return res
class Solution:
def FindGreatestSumOfSubArray(self , array: List[int]) -> int:
# write code here
n=len(array)
dp=[0]*n
dp[0]=array[0]
for i in range(1,n):
dp[i]=max(array[i],dp[i-1]+array[i])
return max(dp)
13.最长回文子串
class Solution:
def getLongestPalindrome(self , A: str) -> int:
# write code here 没看懂
# 短的回文 两边+一样的东西 新回文
# 可以从中间开始
n=len(A)
if n==1:return 1
if n==2:return 2 if A[0]==A[1] else 1
max_length = 1
for i in range(1, len(A)):
even = A[i - max_length:i + 1]
odd = A[i - max_length - 1:i + 1]
if i - max_length >= 0 and even == even[::-1]:
max_length += 1
if i - max_length - 1 >= 0 and odd == odd[::-1]:
max_length += 2
return max_length
class Solution:
def getLongestPalindrome(self , A: str) -> int:
# write code here
res = ''
for i in range(len(A)):
start = max(0,i-len(res)-1)
tmp = A[start:i+1]
if tmp ==tmp[::-1]:
res = tmp
else:
tmp = tmp[1:]
if tmp == tmp[::-1]:
res = tmp
return len(res)
class Solution:
def getLongestPalindrome(self , A: str) -> int:
# write code here
N = len(A)
if A==A[::-1]:return N
max = 1
# range(N)表示range(0,N)【不包含N】,此处指遍历字符串
# 每增加1个字符,就判断以这个字符结尾的子串是不是回文串
for i in range(1,N):
# 当前字符往前max位,再往前还有值
# 【...ABA...】型,往前延伸max+2位,增加的两位是当前字符和对称后的另一头的字符
if i-max >=1 and A[i-max-1:i+1] == A[i-max-1:i+1][::-1]:
max += 2
# 当前字符往前max位,再往前可能没有值了
# 【...AA...】型,往前延申max+1位,增加的1位就是当前字符
elif i-max >=0 and A[i-max:i+1] == A[i-max:i+1][::-1]:
max += 1
return max
14.数字字符串转化成IP地址
class Solution:
def restoreIpAddresses(self , s: str) -> List[str]:
# write code here
# 枚举法
res = []
n = len(s)
i = 1
#遍历IP的点可能的位置(第一个点)
while i < 4 and i < n - 2:
j = i + 1
#第二个点的位置
while j < i + 4 and j < n - 1:
k = j + 1
#第三个点的位置
while k < j + 4 and k < n:
#最后一段剩余数字不能超过3
if n - k >= 4:
k += 1
continue
#从点的位置分段截取
a = s[0: i]
b = s[i: j]
c = s[j: k]
d = s[k:]
#IP每个数字不大于255
if int(a) > 255 or int(b) > 255 or int(c) > 255 or int(d) > 255:
k += 1
continue
#排除前导0的情况
if (len(a) != 1 and a[0] == '0') or (len(b) != 1 and b[0] == '0') or (len(c) != 1 and c[0] == '0') or (len(d) != 1 and d[0] == '0'):
k += 1
continue
#组装IP地址
temp = a + "." + b + "." + c + "." + d
res.append(temp)
k += 1
j += 1
i += 1
return res
递归法
class Solution:
def __init__(self):
self.res = []
self.s = ""
self.nums = ""
#step表示第几个数字,index表示字符串下标
def dfs(self, step: int, index: int):
#当前分割出的字符串
cur = ""
#分割出了四个数字
if step == 4:
#下标必须走到末尾
if index != len(self.s):
return
self.res.append(self.nums)
else:
i = index
#最长遍历3位
while i < index + 3 and i < len(self.s):
cur += self.s[i]
#转数字比较
num = int(cur)
temp = self.nums
#不能超过255且不能有前导0
if num <= 255 and (len(cur) == 1 or cur[0] != '0'):
#添加点
if step - 3 != 0:
self.nums += cur + "."
else:
self.nums += cur
#递归查找下一个数字
self.dfs(step + 1, i + 1)
#回溯
self.nums = temp
i += 1
def restoreIpAddresses(self , s: str) -> List[str]:
self.s = s
self.dfs(0, 0)
return self.res
15.编辑距离(一)
class Solution:
def editDistance(self , str1: str, str2: str) -> int:
# write code here
dp = list(range(len(str2) + 1))
for i in range(len(str1)):
# print(i,dp)
# 这个比较特殊的就是他在0位置放的是当前属于第几次操作 从1开始
tmp = dp.copy()
dp[0] = i + 1
for j in range(len(str2)):
if str1[i] == str2[j]:# 如果当前相同的话就可以直接等于前面拿一个数了
dp[j + 1] = tmp[j]
else:
dp[j + 1] = 1 + min(tmp[j + 1], tmp[j], dp[j])
# 如果不相同就用上一轮的 上一个 这个 和 这一轮的上一个找个最小的 再+1
# 不相同就要+1
# print(dp)
return dp[-1]
16.编辑距离(二)
class Solution:####看不懂
def minEditCost(self , str1: str, str2: str, ic: int, dc: int, rc: int) -> int:
# write code here
# ic插入,dc删除,rc替换
m, n = len(str1), len(str2)
dp = [0] * (n+1)
for j in range(n+1):
dp[j] = j * ic # 先创造一个全都插入的结果
for i in range(1, m+1):
prev = dp[0]
dp[0] = i * dc# 第0个代表删除掉几个数字
for j in range(1, n+1):
temp = dp[j]
if str1[i-1] == str2[j-1]:
dp[j] = prev# 如果相同的话 当前就等于删除掉
else:# 如果不同的话 就是当前j+删除 当前(j-1)+插入 上一轮的j+替换 三选一
dp[j] = min(temp+dc, dp[j-1]+ic, prev+rc)# temp = dp[j]
prev = temp
return dp[-1]
17.正则表达式匹配(内部有特殊符号代表任意字符或者任意次数)
class Solution:
def match(self , str: str, pattern: str) -> bool:
# write code here
m,n=len(str),len(pattern)
x=[[False for i in range(n+1)] for j in range(m+1)]
# 构建一个m+1行n+1列的矩阵 初始化为False
for i in range(m+1):
# 可以把ij看作当前字符子串的长度
for j in range(n+1):
# 从前往后一个一个判断 然后慢慢的搞 最后返回了最后的判断结果
if j==0:
if i==0:
# 00为True就相当于是在表示 若两个空就为True
x[i][j]=True
# pattern不为空的时候
else:
if pattern[j-1]!='*':
# p本轮的上一个不是*的时候会比较简单
if i>0 and (str[i-1]==pattern[j-1] or pattern[j-1]=='.'):
# 确保s有一个字符
x[i][j]=x[i-1][j-1]
# 这里就是再说 我当前的最后一个匹配了 那我整体能不能匹配就看前面的了
else:# pattern[j-1]=='*'
# *重复0次 j-2就不存在了
if j>1: # 这两个if都是要走一遍的 所以是用的或操作
x[i][j] |=x[i][j-2] #|或 x[i][j] = x[i][j] | x[i][j-2] 二者有真取真
# *重复>0次 就直接看上一个str的结果来判断了 因为用了或 所以可以一直这么判断下去
if (j>1 and i>0) and (str[i-1]==pattern[j-2] or pattern[j-2]=='.'):
x[i][j] |=x[i-1][j] #|或
return x[m][n]
18.最长的括号子串
class Solution:# "(()))()"这个情况解决不了
def longestValidParentheses(self , s: str) -> int:
# write code here
stack = [-1]
ans = 0
for i in range(len(s)):
if s[i] == '(':
stack.append(i)
else:
stack.pop()
if stack:# stack为空了
ans = max(ans, i - stack[-1])
else:
stack.append(i)
return ans
19.打家劫舍(一)有时候需要连续放弃两家不偷
class Solution:
def rob(self , nums: List[int]) -> int:
# 展开两列 所以可以使得我们是间隔的 还可以错开两个
# write code here
rob1, rob2 = 0, 0
for num in nums:
rob1, rob2 = rob2, max(rob1 + num, rob2)
return rob2
20.打家劫舍(二)第一家和最后一家是相邻的
class Solution:
def rob(self , nums: List[int]) -> int:
# write code here
if len(nums) == 1:
return nums[0]
l0, r0, l1, r1 = 0, 0, 0, nums[0]
for item in nums[1:]:# 和刚刚首尾不相连的类似 但是在这里直接考虑上考虑首 和不考虑首了
l0, r0 = r0, max(l0+item, r0) # 不偷第一家
l1, r1 = r1, max(l1+item, r1) # 偷第一家
# l1为偷第一家但没有偷最后一家时的状态,r1把最后一家偷后的状态考虑了
# r0为不偷第一家,把最后一家偷后的状态考虑了
return max(l1, r0)
class Solution:
def rob(self , nums: List[int]) -> int:
# write code here
def get_rop(nums):
cur, pre = 0, 0
for num in nums:
cur, pre = max(cur, pre+num), cur
return cur
return max(get_rop(nums[1:]), get_rop(nums[:-1])) if len(nums)!=1 else nums[0]
21.买卖股票的最好时机(一)只买卖各一次
class Solution:
def maxProfit(self , prices: List[int]) -> int:
# write code here
res,x=0,prices[0]# x是买入的价格
for i in range(1,len(prices)):
if prices[i]-x<0:
x=prices[i]
res=max(res,prices[i]-x)
return res
22.买卖股票的最好时机(二)可以多次买卖
这个其实比上一道题要简单一点
class Solution:
def maxProfit(self , prices: List[int]) -> int:
# write code here
# 收益就是上升趋势的线段
# 只需要累加当前与前一个的大小即可
res = 0
m = len(prices)
for i in range(1, m):
# 如果当前比前一天的大,说明可以获取收益
if prices[i] > prices[i-1]:
res += (prices[i] - prices[i-1])
return res
23.买卖股票的最好时机(三)可以买卖各两次 但必须卖空才能买
class Solution:
def maxProfit(self , prices: List[int]) -> int:
n = len(prices)
#初始化dp为最小
dp = [[-10000] * 5 for i in range(n)]
#第0天不持有状态
dp[0][0] = 0
#第0天持有股票
dp[0][1] = -prices[0]
#状态转移
for i in range(1, n):
dp[i][0] = dp[i - 1][0]
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i])
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i])
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i])
dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i])
#选取最大值,可以只操作一次
return max(dp[n - 1][2], max(0, dp[n - 1][4]))
这个不能算是动态规划
class Solution:
def maxProfit(self , prices: List[int]) -> int:
# write code here
num_days=len(prices)
if num_days<2:# 天数少于二 直接输出0
return 0
profit=[0]
max_profit=0
low_price=prices[0]
for i in range(1, num_days):
if(prices[i]<low_price):
low_price=prices[i]
if(prices[i]-low_price>max_profit):
max_profit=prices[i]-low_price
profit.append(max_profit)# 不停的在找当前最大收益值
sum_profit,max_profit,high_price=0,0,prices[num_days-1]
# 这里在初始化
for i in range(num_days-2, 0, -1):
if(prices[i]>high_price):
high_price=prices[i]
if(high_price-prices[i]>max_profit):
max_profit=high_price-prices[i]
if(sum_profit<max_profit+profit[i-1]):
sum_profit=max_profit+profit[i-1]
return sum_profit