LeetCode 139:单词拆分,从前缀是否可达开始

题目要求 输入输出 输入:字符串 s 和字符串列表 wordDict wordDict 中的每个元素都是一个可以使用的单词;同一个单词可以重复使用 输出:如果 s 可以被拆成一个或多个字典单词,返回 True;否则返回 False 拆分必须覆盖整个字符串,不能跳过字符,也不能改变字符顺序 示例 输入:s = "leetcode", wordDict = ["leet", "code"] 输出:True 解释:"leetcode" 可以拆成 "leet" + "code" 输入:s = "applepenapple", wordDict = ["apple", "pen"] 输出:True 解释:"apple" + "pen" + "apple";同一个单词可以重复使用 输入:s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] 输出:False 解释:前面可以拼出一些片段,但最后不能覆盖完整字符串 约束 1 <= s.length <= 300 1 <= wordDict.length <= 1000 1 <= wordDict[i].length <= 20 s 和 wordDict[i] 只包含小写英文字母 wordDict 中的单词互不相同 Step 1:先让“已完成的前缀”变得可见 先只问一个具体问题:对于 s = "leetcode",在确认整串能否拆开之前,我们能不能先记录“已经拆完了哪一段”? ...

2026年9月22日 · 5 分钟 · map[name:Jeanphilo]

LeetCode 213:打家劫舍 II,环形拆成两个线性问题

题目要求 输入输出 输入:整数数组 nums,nums[i] 表示第 i 间房子的金额。 房子首尾相连,不能偷相邻的两间房子,因此第 0 间和第 n - 1 间也不能同时偷。 输出:在不触发警报的前提下,最多能偷到的金额。 约束:1 <= nums.length <= 100,0 <= nums[i] <= 1000。 示例 输入:nums = [2,3,2] 输出:3 解释:第 0 间和第 2 间首尾相邻,不能同时偷;选择金额 3 的房子。 输入:nums = [1,2,3,1] 输出:4 解释:可以偷下标 0 和下标 2,也可以偷下标 1 和下标 3。 输入:nums = [1,2,3] 输出:3 这篇只用 Python,先从最小的首尾冲突开始。 Step 1:先处理首尾相邻的冲突 先问一个具体问题:nums = [2,3,2] 时,能不能沿用直线街道的做法,直接在整个数组上选择不相邻的房子? 当前基线只有一条规则:相邻房子不能同时偷。若把这三个数当成直线,可能会把下标 0 和下标 2 当成彼此不相邻,从而错误地选择两端,得到金额 4。 这在环形输入上就会失败:首尾也相邻,0 和 2 必须互斥。要消除这一个冲突,只增加一个分支规则:任何合法方案至少属于下面一个候选分支。 ...

2026年9月22日 · 4 分钟 · map[name:Jeanphilo]

LeetCode 322:零钱兑换,从更小金额推出完全背包 DP

题目要求 给定一个整数数组 coins,其中 coins[i] 是一种硬币的面额;再给定一个非负整数 amount,要求凑出刚好等于 amount 的金额。 每种硬币都可以使用任意次。 返回凑出 amount 所需的最少硬币数量。 如果无法凑出,返回 -1。 如果 amount = 0,不需要使用硬币,答案是 0。 约束是:1 <= coins.length <= 12、1 <= coins[i] <= 2^31 - 1、0 <= amount <= 10^4。硬币面额互不相同。 例如: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:5 + 5 + 1 一共使用 3 枚硬币。 输入:coins = [2], amount = 3 输出:-1 解释:只使用面额 2 无法凑出 3。 输入:coins = [1], amount = 0 输出:0 解释:目标金额已经是 0,不需要再选硬币。 Step 1:先把目标金额拆成更小的问题 先看一个会逼出问题结构的小例子: ...

2026年9月22日 · 5 分钟 · map[name:Jeanphilo]

LeetCode 300:最长递增子序列,从“以 i 结尾”推出序列 DP

题目要求 输入输出 输入:整数数组 nums 输出:返回 nums 的最长严格递增子序列长度 子序列保持原数组中的相对顺序,但不要求连续,可以跳过元素 如果选择的下标是 i_1 < i_2 < ... < i_k,还必须满足 nums[i_1] < nums[i_2] < ... < nums[i_k] 子序列非空,因此至少选择一个元素 约束:1 <= nums.length <= 2500,-10^4 <= nums[i] <= 10^4 示例 输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:[2,5,7,101] 是一个长度为 4 的严格递增子序列;它不要求在原数组中连续。 输入:nums = [0,1,0,3,2,3] 输出:4 解释:[0,1,2,3] 的长度为 4;`[0,1,3]` 也是合法但更短的选择。 这篇只用 Python,从“每个位置结尾能形成多长”这个局部问题开始,逐步写出可运行解法。 Step 1:先问“以哪个位置结束?” 先看第一个例子: 下标: 0 1 2 3 4 5 6 7 数值: 10 9 2 5 3 7 101 18 答案 [2,5,7,101] 跳过了 9 和 3。当我们走到值为 7 的位置时,不能只问“它左边相邻的数是谁”,而要问: ...

2026年9月22日 · 4 分钟 · map[name:Jeanphilo]

LeetCode 416:分割等和子集,从目标总和推到 0/1 背包倒序遍历

题目要求 给定一个只包含正整数的数组 nums,判断能不能把所有元素分成两个子集,使两个子集的元素总和相等。 输入与输出 输入:整数数组 nums 输出:如果能平分,返回 True;否则返回 False 每个元素必须放入两个子集之一,不能丢弃,也不能把同一个元素使用两次 示例 输入:nums = [1,5,11,5] 输出:True 解释:[1,5,5] 和 [11] 的总和都是 11 输入:nums = [1,2,3,5] 输出:False 解释:总和是 11,奇数不能平分 输入:nums = [1,1] 输出:True 解释:两个子集分别拿一个 1 约束 按 LeetCode 416 题面: 1 <= nums.length <= 200 1 <= nums[i] <= 100 sum(nums) <= 20000 Step 1:先排除总和为奇数的数组 先问一个还不涉及选数的问题:两个整数子集的和相等时,整个数组的总和必须满足什么条件? 问题压力 看 nums = [1,2,3,5]。它的总和是 11。如果两个子集的和都叫 x,那么必须有: 2 * x = 11 整数 x 不可能满足这个等式,所以这个输入可以在尝试任何选数之前直接判定为 False。 ...

2026年9月22日 · 5 分钟 · map[name:Jeanphilo]

LeetCode 338:比特位计数,如何复用较小数字的结果

题目要求 给定一个非负整数 n,返回一个长度为 n + 1 的数组 answer。 其中: answer[i] = 整数 i 的二进制表示中 1 的数量 需要回答的范围包含 0 和 n。 LeetCode 提供的方法接口是: countBits(n: int) -> List[int] 示例 1 输入:n = 2 输出:[0,1,1] 对应关系是: 0 -> 0 -> 0 个 1 1 -> 1 -> 1 个 1 2 -> 10 -> 1 个 1 示例 2 输入:n = 5 输出:[0,1,1,2,1,2] 约束 0 <= n <= 10^5 Step 1:这次要回答 0 到 n 的所有数字 LeetCode 191 只要求统计一个整数。现在看 n = 5: ...

2026年7月15日 · 3 分钟 · map[name:Jeanphilo]

LeetCode 198:打家劫舍,从偷或不偷推出一维 DP

题目要求 输入输出 输入:整数数组 nums nums[i] 表示第 i 间房子的金额 不能偷相邻房子 输出:返回最多能偷到的金额 约束:1 <= nums.length <= 100,0 <= nums[i] <= 400 示例 输入:nums = [1,2,3,1] 输出:4 解释:偷下标 0 和下标 2,金额 1 + 3 = 4 输入:nums = [2,7,9,3,1] 输出:12 解释:偷下标 0、2、4,金额 2 + 9 + 1 = 12 这篇只用 Python,从这个二选一冲突推出一维 DP。 从 [1,2,3,1] 的相邻冲突开始 看例子: nums = [1,2,3,1] 如果偷下标 2 的房子,金额是 3,那么下标 1 和下标 3 都不能偷。 如果不偷下标 2,答案可能来自前面下标 0..1 的最优结果。 所以走到某一间房时,核心选择只有两个: ...

2026年5月3日 · 3 分钟 · map[name:Jeanphilo]

LeetCode 70:爬楼梯,从 dp[i] 含义推出一维 DP

题目要求 输入输出 输入:整数 n 含义:爬到第 n 阶楼顶 每次可以爬 1 或 2 阶 输出:返回到达楼顶的不同走法数量 约束:1 <= n <= 45 示例 输入:n = 2 输出:2 解释:1+1,2 输入:n = 3 输出:3 解释:1+1+1,1+2,2+1 这篇只用 Python,从 dp[i] 的含义一步一步推出最终代码。 从 n = 3 的最后一步开始 先看最小能暴露转移的例子: n = 3 到第 3 阶的最后一步只可能来自: 第 2 阶,再走 1 阶 第 1 阶,再走 2 阶 所以“到第 3 阶的方法数”不是凭空算出来的,而是来自两个更小的位置。 Step 1:先定义更小的问题 直接问“到第 n 阶有几种走法”太大。先定义: dp[i] = 到达第 i 阶的方法数 注意这里的 i 是楼梯位置,不是数组下标含义上的第几个元素。 ...

2026年5月3日 · 4 分钟 · map[name:Jeanphilo]

LeetCode 746:使用最小花费爬楼梯,从 top 位置推出 dp

题目要求 输入输出 输入:整数数组 cost cost[i] 表示踩到第 i 阶的代价 每次可以爬 1 或 2 阶 可以从下标 0 或下标 1 开始 输出:返回到达楼顶的最小花费 约束:2 <= cost.length <= 1000,0 <= cost[i] <= 999 示例 输入:cost = [10,15,20] 输出:15 解释:从下标 1 开始,付 15 后直接到达 top 输入:cost = [1,100,1,1,1,100,1,1,100,1] 输出:6 从 [10,15,20] 的 top 位置开始 先看最小例子: cost = [10,15,20] 楼顶不是下标 2,而是在最后一阶之后的位置,可以记为位置 3。 到达 top 位置 3 的最后一步只可能来自: 位置 2,付 cost[2] 位置 1,付 cost[1] 这题最容易错的地方就在这里:我们要求的是“到达 top 的花费”,不是“到达最后一个下标的花费”。 ...

2026年5月3日 · 3 分钟 · map[name:Jeanphilo]

LeetCode 53:最大子数组和,从 dp[i] 含义推出 Kadane

题目要求 输入输出 输入:整数数组 nums 输出:返回连续非空子数组的最大和 子数组必须连续 至少选一个元素 约束:1 <= nums.length <= 10^5,-10^4 <= nums[i] <= 10^4 示例 输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:[4,-1,2,1] 的和最大,为 6 输入:nums = [1] 输出:1 这篇只用 Python,从 dp[i] 的含义一步一步推出 Kadane 算法。 从 [-2,1,-3,4] 的断点开始 看一个前缀例子: nums = [-2, 1, -3, 4] 如果走到 4,有两种选择: 把前面的某个连续子数组接到 4 前面 从 4 自己重新开始 这里的关键不是“所有子数组怎么枚举”,而是: 当我们决定一个最优子数组必须以当前位置结尾时,它的来源只有两种:接上前一个位置的最优结尾,或者从当前位置重新开始。 这就是一维 DP 的入口。 Step 1:先定义一个更小的问题 直接问“整个数组的最大子数组和是多少”太大。我们先强加一个限制: 如果子数组必须以第 i 个元素结尾,它的最大和是多少? 这个值记为 dp[i]。 这里的 dp[i] 不是“前 i 个元素里的最大子数组和”。它更窄: ...

2026年5月3日 · 3 分钟 · map[name:Jeanphilo]