leetcode 139 && 140. 单词拆分(I II) medium

leetcode 139. 单词拆分  medium          

题目描述:

给定一个非空字符串 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"。
     注意你可以重复使用字典中的单词。

解题思路:

方法一: 回溯,一开始想到这个方法,但是复杂度太高了 O(n^n)

可以用记忆数组来优化,这里我们的记忆数组 memo[i] 定义为范围为 [i, n] 的子字符串是否可以拆分,初始化为 -1,表示没有计算过,如果可以拆分,则赋值为1,反之为0。

方法二: dp O(n^2)

对于这种单一字符串假如用dp求解,一般都是求以每个位置为结尾的子串的最优解,那么这道题也不例外。

状态转移方程: dp【i】表示 0~i-1形成的子串能否由字典dict中的单词组成组成,我们只要枚举0~i-1形成的子串它的所有后缀,看能否在字典中找到对应的单词,并且子串的前半部分也就是 dp【j】也等于true,就ojbk了

代码:

//dp
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        
        unordered_set<string> hash(wordDict.begin(),wordDict.end()); //转为hash表,好找
        
        int n=s.size();
        vector<bool> dp(n+1,false);
        dp[0]=true;
        
        for(int i=1;i<dp.size();++i){  // i是子串长度,也是下标 0~i-1形成的子串
            
            for(int j=0;j<=i-1;++j){ //j:内部子串起点下标 枚举这个子串的后缀
                string str=s.substr(j,i-j);
                if(hash.count(str) && dp[j]){  // 
                    dp[i]=true;
                    break;
                }
            }
        }
        
        return dp.back();
    }
};



//一开始的回溯
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        
        return dfs(s,0,wordDict);
        
    }
    
    bool dfs(string &s,int start,vector<string> &wordDict){
        if(start>=s.size()) return true;
        
        for(auto str:wordDict){
            int len=str.size();
            if(s.substr(start,len)==str){
                if(dfs(s,start+len,wordDict))
                    return true;
            }
        }
        
        return false;
    }
    
};



//用记忆memo优化 回溯
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<int> memo(s.size(), -1);
        return check(s, wordSet, 0, memo);
    }
    bool check(string s, unordered_set<string>& wordSet, int start, vector<int>& memo) {
        if (start >= s.size()) return true;
        if (memo[start] != -1) return memo[start];
        for (int i = start + 1; i <= s.size(); ++i) {
            if (wordSet.count(s.substr(start, i - start)) && check(s, wordSet, i, memo)) {
                return memo[start] = 1;
            }
        }
        return memo[start] = 0;
    }
};

leetcode 140. 单词拆分 II  hard         

题目描述:

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

说明:

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

输入:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
输出:
[
  "cats and dog",
  "cat sand dog"
]

解题思路:

这种要求输出所有解的题,肯定不能用dp来做,得用回溯法,所以我们直接在第一题的递归解上面修改一下就ok了,并且有个小tips,先用dp判断一下有无解,有解再dfs

代码:


class Solution {
public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> hash(wordDict.begin(),wordDict.end()); //转为hash表,好找
        int n=s.size();
        vector<bool> dp(n+1,false);
        dp[0]=true; 
        for(int i=1;i<dp.size();++i){  // i是子串长度,也是下标 0~i-1形成的子串
            
            for(int j=0;j<=i-1;++j){ //j:内部子串起点下标 枚举这个子串的后缀
                string str=s.substr(j,i-j);
                if(hash.count(str) && dp[j]){  // 
                    dp[i]=true;
                    break;
                }
            }
        }
        
        if(dp.back()==false) return {};  // 先判断一下有无解 ,加速代码
        
        vector<string> ret;
        string one;
        dfs(s, one, wordDict, ret, 0);
        return ret;
        
        
    }
    
    
    void dfs(string &s, string &one, vector<string> &wordDict, vector<string> &ret, int cur){
        if (cur == s.size()){
            ret.push_back(one);
            return;
        }

        if (!one.empty())
            one.push_back(' ');
        
        for (auto word: wordDict){
            if (s.find(word, cur) == cur){
                int cur_end = one.size();
                one += word;
                dfs(s, one, wordDict, ret, cur+word.size());
                one.erase(cur_end, word.size());
            }
        }

        if (!one.empty())
            one.pop_back();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值