第8周LeetCode记录
11.5 36. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"
最优解
动态规划的重点:定义状态,和状态转移方程
整个数字的翻译结果数=除去最后1位的部分的翻译结果数*1 + 最后2位的部分的翻译结果数*1
- 定义状态:dp[i]表示nums[0...i]能翻译成字符串的种类数
- 状态转移方程:dp[i] = dp[i-1] + dp[i-2] (如果nums[i-1..i]可以翻译
- 初始化dp[0] = 1
- 输出dp[len-1]
- 优化:当前状态只与前两个状态相关,因此可以使用滚动变量,使用的空间与问题规模无关
我的解
num_str = str(num)
dp = [0] * len(num_str)
dp[0] = 1
# dp[i] = dp[i-1] + dp[i-2]
for i in range(1, len(num_str)):
# 最后1位
dp[i] = dp[i - 1]
if i - 2 < 0:
if 0 <= 10 * int(num_str[i-1])+ int(num_str[i]) <= 25:
dp[i] += 1
else:
# 最后2位
if num_str[i-1] == "0":
continue
if 0 <= 10 * int(num_str[i - 1]) + int(num_str[i]) <= 25:
dp[i] += dp[i - 2]
return dp[-1]
最优解
我的解中空间复杂度为n,优化后只用到3个数,所以用滚动数组进行优化
class Solution {
public int translateNum(int num) {
String src = String.valueOf(num);
int p = 0, q = 0, r = 1;
for (int i = 0; i < src.length(); ++i) {
p = q;
q = r;
r = 0;
r += q;
if (i == 0) {
continue;
}
String pre = src.substring(i - 1, i + 1);
if (pre.compareTo("25") <= 0 && pre.compareTo("10") >= 0) {
r += p;
}
}
return r;
}
}
总结
动态规划要有初始的值,状态变化的方程。有这两点就有思路。
11.6 37. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
思路
动态规划,思路和上一题一样
我的解
滚动数组
class Solution:
@classmethod
def climbStairs(self, n: int) -> int:
f = 1
s = 0
t = 0
for i in range(1,n):
t = s
s = f
f = t + s
if i-2<0:
f += 1
return f
11.7 38. 解码方法
一条包含字母 A-Z
的消息通过以下方式进行了编码:
'A' -> 1
'B' -> 2
...
'Z' -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
题目数据保证答案肯定是一个 32 位的整数。
我的解
len_str = len(s)
first = 0
second = 0
for i in range(len_str):
third = second
second = first
if i == 0:
if int(s[i]) > 0:
first = 1
elif s[i] == "0":
return 0
else:
first = 0
continue
elif i - 2 < 0:
if s[i] == "0" and s[i - 1] == "0":
return 0
if int(s[i]) == 0:
if 1 <= 10 * (int(s[i - 1])) + int(s[i]) <= 26:
first = second
else:
return 0
elif 1 <= 10 * (int(s[i - 1])) + int(s[i]) <= 26:
first = second + third + 1
else:
if int(s[i - 1]) == 0 and int(s[i]) != 0:
# if 1 <= 10 * (int(s[i - 1])) + int(s[i]) <= 26:
# first = third
# else:
# return 0
first = second
elif int(s[i - 1]) != 0 and int(s[i]) == 0:
if 1 <= 10 * (int(s[i - 1])) + int(s[i]) <= 26:
first = third
else:
return 0
elif int(s[i - 1]) == 0 and int(s[i]) == 0:
return 0
elif 1 <= 10 * (int(s[i - 1])) + int(s[i]) <= 26:
first = second + third
return first
总结
特殊 001,2101,301,10的情况。
11.9 39. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
思路
第k个如果选了,最大为dp[k-2] + num[i] ,如果不选第k个,最大为dp[k-1]
我的解
class Solution:
@classmethod
def rob(self, nums: list) -> int:
if not nums:
return 0
size = len(nums)
if size == 1:
return nums[0]
first, second = nums[0], max(nums[0], nums[1])
for i in range(2, size):
first, second = second, max(first + nums[i], second)
return second
11.10 40. 范围求和
给定一个初始元素全部为 0,大小为 m*n 的矩阵 M 以及在 M 上的一系列更新操作。
操作用二维数组表示,其中的每个操作用一个含有两个正整数 a 和 b 的数组表示,含义是将所有符合 0 <= i < a 以及 0 <= j < b 的元素 M[i][j] 的值都增加 1。
在执行给定的一系列操作后,你需要返回矩阵中含有最大整数的元素个数。
输入:
m = 3, n = 3
operations = [[2,2],[3,3]]
输出: 4
解释:
初始状态, M =
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
执行完操作 [2,2] 后, M =
[[1, 1, 0],
[1, 1, 0],
[0, 0, 0]]
执行完操作 [3,3] 后, M =
[[2, 2, 1],
[2, 2, 1],
[1, 1, 1]]
M 中最大的整数是 2, 而且 M 中有4个值为2的元素。因此返回 4。
最优解
给定数组的最小m,n乘积
public class Solution {
public int maxCount(int m, int n, int[][] ops) {
for (int[] op: ops) {
m = Math.min(m, op[0]);
n = Math.min(n, op[1]);
}
return m * n;
}
}