leetcode题解:第139题Word Break

本文详细介绍了如何解决LeetCode第139题——Word Break问题。首先介绍了使用回溯法的解题思路,包括暴力解法的实现及剪枝策略。接着,讲解了动态规划的方法,定义了状态转移方程,并给出了时间复杂度和空间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://leetcode-cn.com/problems/word-break/

解法一、回溯

依照惯例,没有好的思路的时候,就先想想暴力解法怎么做。暴力解法无非是逐个单词地拆分字符串,每次选的单词可以重复。要实现暴力解法的话,肯定就是用回溯,因为回溯算法“撤销选择”的特点,保证了每次都能考虑到所有的单词。
简而言之,对于字符串s和单词word,word长度为n,当word与s[:n]相同时,就可以把word选来进行一次拆分,紧接着继续判断s[n:]是否能被拆分。当s被拆分到长度为0时,回溯算法就找到了答案。
回溯算法是一种穷举法,其时间复杂度自然就会比较高,要使其变得高效的话,采用剪枝是必需的。本题中我们如何剪枝呢?剪枝可以是剪掉那些一定不会得到解的分支,这我们在判断word == s[:n]时就进行了;剪枝也可以是剪掉重复的分支,我们尚未进行。考虑对于s = "leetcode"和单词leet, code, 'le', 'et',选用leet后会进行backtrack("code"),但当我们依次选用'le''et'后,又会进行backtrack("code"),这就存在了重复的分支(因为字典中的单词可以重复使用,即选择列表始终是相同的,只有要判断的s不同)。
要实现这种剪枝也容易,判断backtrack("s")是否被执行过即可。

代码
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> set;
        backtrack(s, wordDict, set);
        return result;
    }
private:
    bool result = false;
    void backtrack(string& s, vector<string>& wordDict, unordered_set<string>& set) {
        if (s.size() == 0) {
            result = true;
            return;
        }
        for (auto& word : wordDict) {
            int n = word.size();
            string s1 = s.substr(0, n);
            if (s1 == word) {
                s = s.substr(n);
                if (!set.count(s)) {
                    backtrack(s, wordDict, set);
                    set.insert(s);
                }
                s = s1 + s;
            }
        }
    }
};

解法二、动态规划

动态规划解法要难想一点,但找到状态的表示后实现起来也不难了。
定义dp[i]:s的前i个字符组成的子串是否能被字典中的单词拆分
状态转移方程:对于长度为m的word,如果s[:i]最后长度为m的子串等于word,那么dp[i]自然就由dp[i - m]决定
初始状态:dp[0] = true,空串可以被拆分,其余的dp[i] = false

代码
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int n = s.size();
        bool dp[n + 1];
        memset(dp, false, sizeof(dp));
        dp[0] = true;
        for (int i = 1; i <= n; ++i) {
            for (auto& word : wordDict) {
                int m = word.size();
                if (i >= m && s.substr(i - m, m) == word) dp[i] = dp[i] | dp[i - m];
            }
        }
        return dp[n];
    }
};
复杂度分析

时间复杂度 O ( n m ) O(nm) O(nm),n为字符串s长度,m为wordDict大小。空间复杂度 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值