[LeetCode] 139、单词拆分

题目描述

给一个字符串,和一些单词,问字符串能不能由这些单词构成。每个单词可以用多次,也可以不用。

示例:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"

解题思路

  • 刚开始是超级暴力的想法,利用回溯法(找全排列),用 wordDict 去生成所有可能的字符串。期间如果出现了目标字符串 s,就返回 true。(这种方法基本毫无疑问会超时)

  • 动态规划:这里最重要的是要想明白如何确定“状态表示”(即状态应该定义为“什么的状态”,参考53、300题),然后如何状态转移(如何由已知子问题的最优解得到大问题的最优解)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c7p62KlC-1576748867350)(./imgs/20191219155226.jpg)]

在这里插入图片描述

其实下面那个练习和第一种dp思路一致,不需要看。

注:动态规划和记忆化搜索本质没区别,《算法导论》中统称为“动态规划”。(只是方向不一样,在评论区

动态规划是打表格法,自底向上解决问题,其特点是:每一次遇到大问题的时候,与这个大问题有关的所有小问题都得到了解决。

记忆化搜索是自顶向下解决问题,每一次遇到大问题的时候,与这个大问题有关的小问题未必都得到了解决,但是解决了一个我们就记录一下,以防止以后再遇到同样问题的时候从头开始再计算一次。

我想“自底向上”与“自顶向下”是它们最主要的区别:

1、“自顶向下”是人的视角,我们面对问题的时候,是直接解决问题,遇到一个问题解决一个问题,所以需要记忆,就像我们工作中遇到问题要写个笔记或者博客记录下来一样。

2、“自底向上”有些资料上说是开启了上帝视角,我很认同这种说法。显然动态规划也是用空间换时间,也得记录,从最小的问题开始做,直到解决了最终的大问题。用动态规划我个人觉得比较好的一点是,解决问题的方向比较有规律,因此状态压缩就成为了可能,不像记忆化搜索那样,解决的问题到处跑。

有的资料,例如《算法导论》对它们二者就不区分,都叫动态规划。

参考代码

class Solution {
public:
    bool wordBreak(string str, vector<string>& wordDict) {
        int length = str.length();
        if(length == 0)
            return false;

        unordered_set<string> uset(wordDict.begin(), wordDict.end());
        bool dp[length];
        memset(dp, 0, sizeof(dp));

        if(uset.count(str.substr(0, 1)) > 0)
            dp[0] = true;

        for(int i = 1; i < length; i++){
            if(uset.count(str.substr(0, i+1)) > 0){
                dp[i] = true;
                continue;
            }

            for(int j = 0; j < i; j++){
                if(dp[j] && uset.count(str.substr(j+1, i-j)) > 0){
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[length - 1];
    }

};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值