前言
所有题目均来自力扣题库中的hot 100,之所以要记录在这里,只是方便后续复习
139.单词拆分
题目:
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。
示例 2:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以由 “apple” “pen” “apple” 拼接成。
注意,你可以重复使用字典中的单词。
示例 3:
输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false
解题思路:
【动态规划】正常思路是我们将目标字符串按每个位置进行拆分,如果前部半分符合了字典里的值,再判断后面的字符串是否能被拼接出来,这很容易联想出递归,是的刚开始就是这么写的,超时了;那我们换个思路,依然是将目标字符串拆分,我们判断后半部分是否符合字典里值,那前半部分怎么办呢?如果我们从字符串起始位置开始递推,并将每个位置对应的结果用数组存起来,那前部分的值就可以直接从该数组中取了,也就是动态规划的思想;每个位置都从头遍历到当前位置,以此为分割位,前半部分从结果数组中取,后部分看字典中是否有,两者都是True则说明当前位置可以被拼成,将结果添加到列表用于后面递推;那初始值是什么呢?可以是字符串索引-1的地方,结果一定是True;另外为了方便判断字典里是否包含某个字符串,可以用hash表
代码(python):
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
results = []
results.append(True)
for i in range(0, len(s)):
flag = False
for j in range(0, i + 1):
if results[j] and s[j : i + 1] in wordDict:
flag = True
break
results.append(flag)
return results[-1]
代码(java):
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
//将字符串集合转化为 hash表
HashSet<String> wordSet = new HashSet<>(wordDict);
//定义结果数组,注意长度是 字符串长度 + 1 ,从0开始
//索引i代表 字符串中i-1索引位置结果值
boolean[] result = new boolean[s.length() + 1];
//定义初始值,当前位置为字符串索引-1位置时,结果为true
result[0] = true;
//从字符串索引0位置 到 字符串索引length - 1位置 依次递推结果值
for(int i = 1; i <= s.length(); i ++){
//定义当前结果
boolean curResult = false;
// 从索引0开始 到 当前索引,依次遍历
for(int j = 0; j <= i; j ++){
//判断 从索引j 将 当前有效字符串切割成 j-1部分 和 j到i部分
//j-1部分是否能组成的结果即结果数组中j位置的值, j到i部分的结果是判断字符串hash中是否包含这个字符串
if(result[j] && wordSet.contains(s.substring(j, i))){
//若找到了这个j 即分割线,说明存在这种组成方式可以拼成 当前0到i - 1的字符串
curResult = true;
break;
}
}
// 将i- 1字符串的结果值放入结果数组中
result[i] = curResult;
}
//最后返回结果数组中最后一位即可
return result[s.length()];
}
}
知识点:
- java中hashset.contains(val)判断hashset是否包含该值;string.substring(start,end)截取字符串,包括start索引的值,不括end索引的值
原题链接:单词拆分
152.乘积最大子数组
题目:
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
解题思路:
【动态规划】我们以某个位置为数组的最后一位,当前位置的乘积就等于前一个位置的乘积*当前值,那当前位置的乘积的最大值就等于前一个位置的最大值?这种情况的前提是所有数组都是正数,若有负数就要分两种情况考虑:情况一是如果当前值是正数,那我们期望前面位置的乘积是越大越好,这样乘积才最大,但是如果前面乘积最大也还是负数呢,这样我们就舍弃前面乘积只保留当前值;情况二是当前值是负数,那我们期望前面位置的乘积越小越好,这样乘积才最大,但是如果前面位置的最小乘积也还是正数呢,这样我们就舍弃前面乘积只保留当前值;综上,当前位置乘积的最大值应该是 前面位置乘积最大值乘当前值、前面位置乘积最小值乘当前值和当前值三者中的最大值;每个位置乘积最大值只与前一个位置乘积最大值和最小值有关,我们可以考虑用动态规划,用两个数组存储当前位置的乘积最大值与最小值,依次递推截止到数组最后一位的乘积最大值,在这个过程中要始终对比并保留出最大的乘积最大值,这也就是答案;那动态规划的初始值是什么呢?就是索引0位置时,乘积最大值和最小值都是当前值;另外由于当前位置的值只和前一个位置的值有关,可以用滚动变量的方式代替数组的方式以节省空间复杂度
代码(python):
class Solution(object):
def maxProduct(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# 定义结果值
result = nums[0]
# 定义最大乘积结果列表 和 最小乘积结果列表 每个位置代表截止到当前数组对应位置的 乘积的最大值 和 最小值
max_result = list()
min_result = list()
# 定义初始值 当索引为0时, 乘积最大值 和最小值 都是他本身
max_result.append(nums[0])
min_result.append(nums[0])
# 递推每个位置的乘积 最大值 和 最小值
for i in range(1, len(nums)):
# 当前位置的 最大值为 前一个位置的最大值*当前值 、前一个位置的最小值*当前值、 当前值 三者的最大
max_value = max(max(max_result[i - 1] * nums[i], min_result[i - 1] * nums[i]), nums[i])
# 当前位置的 最小值为 前一个位置的最大值*当前值 、前一个位置的最小值*当前值、 当前值 三者的最小
min_value = min(min(max_result[i - 1] * nums[i], min_result[i - 1] * nums[i]), nums[i])
# 更新结果值
result = max(result, max_value)
# 将当前位置最大值 和 最小值添加到列表中,用于后面递推
max_result.append(max_value)
min_result.append(min_value)
return result
代码(java):
class Solution {
public int maxProduct(int[] nums) {
int max = nums[0];
int min = nums[0];
int result = nums[0];
for(int i = 1; i < nums.length; ++i){
int lastMax = max;
int lastMin = min;
max = Math.max(lastMax * nums[i], Math.max(lastMin * nums[i], nums[i]));
min = Math.min(lastMax * nums[i], Math.min(lastMin * nums[i], nums[i]));
result = Math.max(result, max);
}
return result;
}
}
知识点:
- 动态规划,如果当前位置的值只和前一个位置的值有关,可以用滚动变量的方式代替数组
原题链接:乘积最大子数组
198.打家劫舍
题目:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
解题思路:
【动态规划】每个位置都有两种情况考虑:第一种情况是偷取,若是偷取的话上一个位置就是未偷取,此时的金额就是当前值+上一个未偷取值。第二种情况是未偷取,那上一个位置可以是偷取的也可以是未偷取的,此时的金额就是上一个位置偷取情况未偷取情况的金额中较大值。所以每个位置金额与上一个位置偷取和未偷取情况有关,我们可以定义两个变量存储起始位置的偷取和未偷取金额,依次递推每个位置的偷取金额和未偷取金额,并保留最大值作为结果值,同时更新两个变量用于下一次递推,也就是动态规划;那起始值是什么呢?索引0位置的偷取值是当前值,未偷取值为0
代码(python):
class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) == 0:
return 0
if len(nums) == 1:
return nums[0]
if len(nums) == 2:
return max(nums[0], nums[1])
results = []
results.append(nums[0])
results.append(max(nums[0], nums[1]))
for i in range(2, len(nums)):
results.append(max(results[i - 1], results[i - 2] + nums[i]))
print results
return results[len(nums) - 1]
代码(java):
class Solution {
public int rob(int[] nums) {
// 定义结果值
int result = nums[0];
// 定义初始值 第一天偷了的情况是数组值,没偷的情况是0 滚动变量
int lastSteal = nums[0];
int lastNoSteal = 0;
// 从索引1开始递推每个位置的偷取 和 不偷取两种情况下的 金额
for(int i = 1; i < nums.length; i ++){
// 当前偷取的值 为 上一个位置没偷 + 当前值
int curSteal = lastNoSteal + nums[i];
// 当前没偷取的值 为 上一个偷取 和 上一个位置没偷 的较大值
int curNoSteal = Math.max(lastSteal, lastNoSteal);
// 更新结果值,取最大的
result = Math.max(Math.max(result, curSteal), curNoSteal);
// 更新 上一次的值 用于下一个位置的递推
lastNoSteal = curNoSteal;
lastSteal = curSteal;
}
return result;
}
}
知识点:
- 动态规划,如果当前位置的值只和前一个位置的值有关,可以用滚动变量的方式代替数组
原题链接:打家劫舍
221.最大正方形
题目:
在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内,找到只包含 ‘1’ 的最大正方形,并返回其面积。
示例 1:
输入:matrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,“1”,“0”]]
输出:4
示例 2:
输入:matrix = [[“0”,“1”],[“1”,“0”]]
输出:1
示例 3:
输入:matrix = [[“0”]]
输出:0
解题思路:
【动态规划】对于每个位置,我们可以把它当作能够组成的正方形的右下角;求其对应的最大面积,也就是求其对应的最大正方形边长;第一列和一行的最大正方形边长只能1是0,那我们能不能根据这些初始值递推中每个位置的最大正方形边长呢?如果当前位置值是0,那么就不存在以该位置为右小角的正方形,最大边长也就是0,如果当前位置值是1,最大正方形边长等于该位置的上方、左侧、左上角位置的最大正方形边长的最小值+1;就这样递推出每个位置的最大正方形边长,并计算出最大面积更新保留
代码(python):
class Solution(object):
def maximalSquare(self, matrix):
"""
:type matrix: List[List[str]]
:rtype: int
"""
max_area = 0
length_i = len(matrix)
length_j = len(matrix[0])
for i in range(0, length_i):
for j in range(0, length_j):
if matrix[i][j] != "0":
now_length = 1
flag = True
while flag and i + now_length < length_i and j + now_length < length_j:
for x in range(0, now_length + 1):
for y in range(0, now_length + 1):
if matrix[i + x][j + y] != "1":
flag = False
if flag:
now_length += 1
max_area = max(max_area, (now_length * now_length))
return max_area
代码(java):
class Solution {
public int maximalSquare(char[][] matrix) {
// 定义结果值
int result = 0;
// 定义网格,与参数网格一一对应,每个位置值代表 以该位置为右小角的最大的正方形的边长
int[][] size = new int[matrix.length][matrix[0].length];
// 从头开始递推每个位置的最大正方形边长
for (int i = 0; i < matrix.length; i ++){
for (int j = 0; j < matrix[0].length; j ++){
// 如果当前值是1,说明该值可以当作正方形右下角;若为0 无需处理,其最大正方形边长也就是0
if(matrix[i][j] == '1'){
// 判断一下是不是 第一行和第一列,若是最大正方形边长只能是1,也就是递推的起始值
if (i == 0 || j == 0){
size[i][j] = 1;
// 最大正方形边长 等于 上方 左侧 左上角 三个位置的最大正方形边长的最小值 再加上 1
}else{
size[i][j] = Math.min(Math.min(size[i - 1][j], size[i][j - 1]), size[i - 1][j - 1]) + 1;
}
// 求此位置为右下角 最大正方形面积,并更新结果值
result = Math.max(result, size[i][j] * size[i][j]);
}
}
}
return result;
}
}
知识点:
- 无
原题链接:最大正方形
279.完全平方数
题目:
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9
解题思路:
【动态规划】每个数n都是由小于等于根号n的某个数 j 的平方 和 另外一个数(n - j * j)组成,那组成的完全平方数数量就是 1 + (n-j*j)的平方数数量,那最少数量也就是n - j*j的最下数量 + 1,也就是说n的结果值和 n-j*j的结果值有关,而j是从1开始到根号n,这种当前结构值和之前某个结构值有关系可以考虑动态规划;那初始值是什么呢?当j*j正好等于n时,也就是n恰巧是一个完全平方数,那么n-j*j就应该是0,所以初始值应该是0,0的平方数数量应该是0,这样1 + 0等于1才是一个完全平方数的最少数量;所以我们可以从1开始递推到n的最小数量,每次j都从1开始到根号n,算出每种情况的最少数量,在这个过程中保留最小的值即当前值的最少数量
代码(python):
class Solution(object):
def numSquares(self, n):
"""
:type n: int
:rtype: int
"""
res = []
res.append(0)
res.append(1)
for i in range(2, n + 1):
min_val = sys.maxsize
j = 1
while j * j <= i:
min_val = min(1 + res[i - j * j], min_val)
j += 1
res.append(min_val)
return res[n]
代码(java):
class Solution {
public int numSquares(int n) {
// 定义结果列表
int[] result = new int[n + 1];
// 起始值 n若为0 最小需要0个完全平方数
result[0] = 0;
// 从1开始递推n的最小个数
for (int i = 1; i < result.length; i ++){
//定义最小个数
int min = Integer.MAX_VALUE;
// j 从1开始,到根号i结束,假设组成i的是 j的平方和 i - j*j 那最小个数就是 1(j本身) + i - j*j的最小个数(在结果列表中取就行)
// 记录这个过程中的最小个数
for(int j = 1; j * j <= i; j ++){
min = Math.min(min, 1 + result[i - j * j]);
}
//记录i的最小个数,用于后续递推
result[i] = min;
}
return result[n];
}
}
知识点:
- 无
原题链接:完全平方数