代码随想录算法训练营第五十四天(完全背包篇)| 279. 完全平方数,139. 单词拆分

本文介绍了如何使用动态规划解决LeetCode中的两道题目:找到总和为给定整数的最小完全平方数个数,以及判断一个字符串能否由给定单词列表中的单词拼接而成。通过构建dp数组并定义递推规则,解决了背包问题与字符串组合问题。
摘要由CSDN通过智能技术生成

279.完全平方数

题目链接:279. 完全平方数 - 力扣(LeetCode)

思路

这道题和上次的零钱兑换很像:代码随想录算法训练营第五十三天(完全背包篇)| LeetCode 322. 零钱兑换-CSDN博客

把完全平方数看作物品,可取用次数不限,总和n看作背包容量,这道题就是求将背包装满的最小物体个数。

1. dp数组定义

dp[j]: 总和为j的平方数的最小个数。

2. 确定递推公式

遍历到数i时,如果用它的平方,可能的最小个数为dp[j - i**2] + 1(加上它本身),如果这个值小于不加上它的平方时的值dp[j],就用它,否则保持不变。所以得到:

dp[j] = min(dp[j], dp[j - i**2] + 1)

3. 初始化

j为0时,根据递推公式,dp值应为0:如果j本身是完全平方数,那么它的dp值应该是1,即它的根,而当i遍历到j的根,根据公式:

dp[j] = min(dp[j], dp[j - i**2] + 1) = min(dp[j], dp[0] + 1),因此,dp[0]需要为0。对于非零下标,dp值应该为正无穷,才能保证在递推的时候才不会被初始值覆盖。

4. 遍历顺序

由于是求物体个数,不涉及遍历物体的顺序,所以可以外层遍历物体内层遍历背包,也可以相反。

5. 举例推导dp数组

以n = 5为例, dp状态图如下:

代码实现

class Solution(object):
    def numSquares(self, n):
        dp = [float('inf')] * (n + 1)
        dp[0] = 0
        for j in range(1, n+1):
            for i in range(1, int(j ** 0.5) + 1):
                if i**2 <= j:
                    dp[j] = min(dp[j], dp[j-i**2] + 1)                
                               
        return dp[n]

139. 单词拆分

题目链接:139. 单词拆分 - 力扣(LeetCode)

思路

1. dp数组定义

dp[j]: 长度为j的字符串能被字典中的单词组成。

2. 确定递推公式

如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。所以递推公式为:

if dp[i] and (s[i:j] in wordSet):
    dp[j] = True

3. 初始条件

dp[i]的状态依赖于前面dp[j]的状态,那么dp[0]一定为true,否则之后就全是False了。

4. 遍历顺序

之前提到过,求排列应该外层遍历背包容量,内层遍历物体;求组合应该外层遍历物体,内层遍历背包容量。这道题是求排列,因为给定单词的每种组合含义是不一样的,都要考虑到是否符合。

e.g. 对字典[''life'', “nice’'], 我们要看是否能组成''nice life'',如果先遍历物体,先看‘life’,发现s没有从0开始的区间里含“life”,就再遍历‘nice’,发现s前面有’nice‘出现,于是继续看s后面的部分是否出现在字典里,但‘life’上一轮已经遍历过,不会重复了,所以结果显示False,但实际应该为True。

5. 举例推导dp[i]

以输入: s = "leetcode", wordDict = ["leet", "code"]为例,dp状态如下:

代码实现

class Solution(object):
    def wordBreak(self, s, wordDict):
        wordSet = set(wordDict)
        dp = [False]*(len(s) + 1)
        dp[0] = True
        for j in range(1, len(s) + 1):
            for i in range(j):
                if dp[i] and (s[i:j] in wordSet):
                    dp[j] = True
                    break
        
        return dp[len(s)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值