139 单词拆分 140 单词拆分II

19 篇文章 0 订阅
13 篇文章 0 订阅

题目

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

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

示例 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

  • 发法一:动态规划
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> dict;//存储字典,方便判断当前字符串是否为给定的单词
        for(int i=0;i<wordDict.size();i++)
            dict.insert(wordDict[i]);
        //s有n个字符,有n+1个切割点,即0~n
        vector<bool> dp(s.size()+1,false);
        dp[0]=true;//边界,s为空串
        //dp[i]=dp[j]+isWord(s[j,i-1])
        //从1开始枚举i,内层循环判断i之前的字符能否全部拆分成给定单词
        for(int i=1;i<=s.size();i++)
        {
            for(int j=0;j<i;j++)
            {//如果dp[j]为true表示j之前字符可以拆分成单词,此时如果j~i-1也是单词则为true
                if(dp[j]&&dict.find(s.substr(j,i-j))!=dict.end())
                {
                    dp[i]=true;
                    break;
                }
            }
        }
        return dp[s.size()];//返回结果
    }
};
  • 时间复杂度O(n^2)
  • 空间复杂度O(n)
  • 思路
    • 当前字符串是否可以拆分成多个单词,转化为当前字符串从头开始去掉几个单词后,剩下的字符串是字典中的单词吗
    • dp[i]=dp[j]+s[j,i-1]

进阶:返回所有字符串拆分成单词后组成的句子

给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。

注意:词典中的同一个单词可能在分段中被重复使用多次。

示例 1:

输入:s = “catsanddog”, wordDict = [“cat”,“cats”,“and”,“sand”,“dog”]
输出:[“cats and dog”,“cat sand dog”]
示例 2:

输入:s = “pineapplepenapple”, wordDict = [“apple”,“pen”,“applepen”,“pine”,“pineapple”]
输出:[“pine apple pen apple”,“pineapple pen apple”,“pine applepen apple”]
解释: 注意你可以重复使用字典中的单词。
示例 3:

输入:s = “catsandog”, wordDict = [“cats”,“dog”,“sand”,“and”,“cat”]
输出:[]

来源:力扣(LeetCode)

class Solution {
public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
    	//此处与题一完全一致
        unordered_set<string> dict;
        for(int i=0;i<wordDict.size();i++)
            dict.insert(wordDict[i]);
        //word[i][j]为true表示字符串s[i,j]是一个单词
        vector<vector<bool>> word(s.length()+1,vector<bool>(s.length(),false));
        vector<bool> dp(s.size()+1,false);
        dp[0]=true;//边界,s为空串
        for(int i=1;i<=s.size();i++)
        {
            for(int j=0;j<i;j++)
            {
                if(dp[j]&&dict.find(s.substr(j,i-j))!=dict.end())
                {//第一处不同,由于要返回全部解,此处在找到一种情况后不能break,还要继续找
                    dp[i]=true;
                    //j是字符串子串的起点字符的下标,i-1是终点字符的下标,且该子串是一个单词
                    word[j][i-1]=true;
                }
            }
        }
        //path储存一个结果,result储存所有结果
        vector<string> path,result;
        //只有s可以分割为单词串时才搜索全部结果
        if(dp[s.size()]) dfs(s,word,s.length(),path,result);
        return result;
    }
	
	//搜索组装所有句子
	//0~index表示当前还未被搜索的字符,在这些字符中寻找s[i,index-1]为单词的i,然后进行下一次递归
    void dfs(const string &s, const vector<vector<bool>> &word, int index,vector<string> &path,vector<string> &result)
    {
        if(index==0)//递归边界,找到一种情况,将path转化为字符串
        {
            string tmp;
            for(int i=path.size()-1;i>0;i--)
                tmp+=(path[i]+" ");
            tmp+=path[0];
            result.push_back(tmp);
            return;
        }
        //尝试所有可能的s[i,index-1]
        for(int i=0;i<index;i++)
        {
            if(word[i][index-1])//如果是一个单词就将它加入到path中,进行下一次递归
            {
                path.push_back(s.substr(i,index-i));
                dfs(s,word,i,path,result);
                path.pop_back();
            }
        }

    }
};
  • 时间复杂度O(n^2)
  • 空间复杂度O(n^2)
  • 思路
    • 第一题只返回所有的切割数,第二题要返回所有的切割结果。如果尝试切割在最后几个字符处失败,前面切割存储单词花费的时间会全部浪费,最终导致超时。故先寻找所有可能的切割位置,将他们存储在word数组中,再通过dfs将所有满足的结果组装起来
    • dp数组与题一几乎一致,只需修改找到一种情况时,要将当前切割的单词记录在word数组中。由于要找到所有情况,将break删除,固定右边界,找到一种情况后继续找下一种情况。
    • dfs使用index判断递归边界。index是当前待切割区间的右边界,左边界为0,当index等于0时就到达了递归边界,此时将path转化为字符串添加到result中。注意dfs是在s串中从后往前寻找单词的,翻译时要从后往前转化。寻找s[0,index-1]范围内所有使s[i,index-1]为单词的情况,如果找到就递归调用,寻找前一个单词。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值