单词拆分系列

57 篇文章 0 订阅
7 篇文章 0 订阅

Leetcode 139. 单词拆分

问题描述

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:

输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。 [ 1 ] ^{[1]} [1]

解题报告

动态规划解决,dp[i] 表示以第 i 个字符结尾的字符串是否是可拆分的。
那么如何求 dp[i] 呢?如果我们能找到第 j 个字符,使得以第 j 个字符结尾的字符串是可拆分的且第 j 个字符到第 i 个字符组成的字符串能够在字典中找到,那么以第 i 个字符结尾的字符串是可拆分的。

实现代码

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int n=s.size();
        set<string>setWords;
        for(int i=0;i<wordDict.size();i++){
            setWords.insert(wordDict[i]);
        }
        vector<int>dp(n+1,0);
        dp[0]=1;
        for(int i=1;i<=n;i++){
            for(int j=i-1;j>=0;j--){
                if(dp[j]&&setWords.find(s.substr(j,i-j))!=setWords.end()){
                    dp[i]=1;
                    break;
                }
            }
        }
        return dp[n];
    }
};

Leetcode 140. 单词拆分 II

问题描述

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

说明:

分隔时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:

输入:
s = “catsanddog”
wordDict = [“cat”, “cats”, “and”, “sand”, “dog”]
输出:
[
“cats and dog”,
“cat sand dog”
]
[ 2 ] ^{[2]} [2]

解题报告

上一题是判断是否可分,这道题需要输出具体的单词拆分方式。直觉上按照上一题的动态规划 是不怎么好解决。【UP:其实动态规划是可以实现的,是我太愚昧\了,定义 vectordp[] ,dp[i] 表示以第 i 个字符开始的切分方式,具体参考官方题解】

最好的方式是采用DFS+回溯。

具体怎么操作呢?

  • 从第 0 个字符开始搜索,当存在 j 使得 s[0;j] 在字典中能够找到,然后从 j+1 个单词开始搜索,当搜索到底时看能否找到一个单词的拆分方式。
  • 这种方法明显是需要回溯的,从第 0 个字符开始搜索,有可能找到第 i 个字符使得s[0;i] 在字典中能够找到,那么接下来就可以从 i+1 个单词开始搜索。

这种深搜加回溯的方法还是存在很多重复搜索的。比如 词典是:["cat", "cats", "and", "sand", "dog"],字符串是 catsanddog,一条路径是:(1)从 c 开始,到 t 结束,然后从 s 出发,到 d 结束,最后从 d 出发,到 g 结束;另一条路径是:(2)从 c 开始,到 s 结束,然后从 a 开始,到 d 结束,最后从 d 开始,到 g 结束。
观察上面这两条路径发现两条路径中最后都球了一遍【从 d 开始,到 g 结束】。最好的优化方法是 记忆化,如果从第 i 个字符开始的切分方式已经求过,那么直接拿出,就不继续递归到从 i+1 个字符开始。 [ 3 ] ^{[3]} [3]

实现代码

  • 我实现的 深搜+DFS,TLE
class Solution {
public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        vector<string>ans,curStr;
        set<string>wordSet;
        for(int i=0;i<wordDict.size();i++)wordSet.insert(wordDict[i]);
        dfs(ans, s, 0, 0, curStr, wordSet);
        return ans;
    }
    void dfs(vector<string>&ans, string s, int st, int ed, vector<string> curStr, set<string>&wordSet){
        if(ed>=s.size()){
            string tmp;
            for(int i=0;i<curStr.size()-1;i++) tmp+=curStr[i];
            ans.push_back(tmp);
        }
        else{
            for(;ed<s.size();ed++){
                if(wordSet.find(s.substr(st,ed-st+1))!=wordSet.end()){
                    curStr.push_back(s.substr(st, ed-st+1));
                    curStr.push_back(" ");
                    dfs(ans, s, ed+1, ed+1, curStr, wordSet);
                    curStr.pop_back();
                    curStr.pop_back();
                }
            }
        }
    }
};
  • 官方题解,直接深搜。
class Solution {
    public:
        vector<string> wordBreak(string s, vector<string>& wordDict) {
            set<string>wordSet;
            for(int i=0;i<wordDict.size();i++)wordSet.insert(wordDict[i]);
            word_Break(s, wordSet, 0);
            return map[0];
        }
        unordered_map<int, vector<string>> map;
        vector<string> word_Break(string s, set<string> wordDict, int start) {
            if (map.find(start)!=map.end()){
                // cout<<start<<" ";
                return map[start];
            }
            vector<string> res;
            # 如果不加这两段代码的话,当从 size 个字符开始搜索时,搜索结果为空,返回 
            # list为空,这样的话,从上一个字符开始搜索的结果就无法取到。
            # res.push_back(s.substr(start, end-start+1) + (l=="" ? "" : " ") + l);不会执行
            if (start == s.size()) {
                res.push_back("");
            }
            for (int end = start; end <= s.length(); end++) {
                if (wordDict.find(s.substr(start, end-start+1))!=wordDict.end()) {
                    vector<string> list = word_Break(s, wordDict, end+1);
                    for (auto l : list) {
                        res.push_back(s.substr(start, end-start+1) + (l=="" ? "" : " ") + l);
                    }
                }
            }
            map[start]=res;
            return res;
    }
};

// 作者:LeetCode
// 链接:https://leetcode-cn.com/problems/word-break-ii/solution/dan-ci-chai-fen-ii-by-leetcode/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

参考资料

[1] Leetcode 139. 单词拆分
[2] Leetcode 140. 单词拆分 II
[3] Leetcode 140. 单次拆分 II:官方题解

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值