力扣-动态规划-139. 单词拆分 背包问题本质分析

力扣-动态规划-139. 单词拆分 背包问题本质分析

279. 完全平方数

题目描述

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-break
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例 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

提示:

1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s 和 wordDict[i] 仅有小写英文字母组成
wordDict 中的所有字符串 互不相同

解题思路:动态规划 完全背包问题

 这道题很有意思,首先想到的办法是用回溯法暴力求解一遍,但会超时,然后考虑将前面的结果保存起来在后面用,就很容易联想到用动态规划。因为动态规划之所以比暴力搜索时间复杂度低就是因为动态规划能够保存前面遍历的结果并用在后面,那么属于普通动态规划还是背包问题呢?字典中的每个单词可以使用无限次,给定的字符串长度和内容是固定的,因此属于动态规划中的完全背包问题。这道题尽管看起来不太像背包问题,但它的本质和背包问题是一样的。都是记录前面的结果,并用前面的结果来推导后面的结果,同时能够用一个二维数组的遍历过程来模拟结果的推导。
 既然核心是用二维数组的遍历来模拟结果的推导,那么我们就可以用一个完整的二维数组来实现推导过程,也可以用一维数组来模拟每一行的推导。所以背包问题可以用二维数组,也可以用一维数组。
 在模拟二维数组的遍历过程中就有两种遍历顺序,二维数组的两个维度分别先遍历会产生不同的效果。举个例子,假设内层遍历的是物品nums[i],外层遍历的是背包容量,那么所有的物品在确定的背包容量 j 条件下会依次遍历一遍,就可以理解为每次遍历物品的时候所有物品都处在同一层;当外层遍历的是物品,内层遍历的是背包容量时,每次遍历到一个物品nums[i]时,会在所有的背包容量下尝试能否把物品nums[i]加入到背包中,nums[i]有两种选择,加入或者不加入,加入的话需要考虑容量为 j - nums[i]时能否满足,不加入时则不作处理。
 在本题中,字典中的所有单词在固定的字符串 str 条件下都需要遍历一遍才能不遗漏任何一种情况。因此外层遍历背包容量,内层遍历物品。遍历的时候,如果内层字典中的某个单词word刚好能够匹配到 str 的结尾部分,如果 str 在去掉 结尾 word 的剩下字符串也能够被字典中单词拼凑成,那么str一定可以被字典中单词拼凑成,这个时候就不需要在接着遍历剩下的物品了。
 代码如下,各位小伙伴如果有什么问题可以在评论里提出来,欢迎大家交流。

 //完全背包
    //对于不同长度的字符串,字典中的单词处在同一级
    public boolean wordBreak(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int j = 1; j <= s.length(); j++) {//字符串长度
            for (int i = 0; i < wordDict.size(); i++) {
                String str = new String(s.substring(0, j));//每次截取长度为j的子串
                String word = wordDict.get(i);
                //判断单词wordDict.get(i)是否出现在长度为j的子串中的最末尾以及子串剩下的部分是否能够用字典中的单词拼凑成
                if (j >= word.length() && str.substring(j - word.length()).equals(word)) {
                    dp[j] = dp[j] || dp[j - word.length()];
                }
            }
        }
        return dp[s.length()];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值