139. 单词拆分

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

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


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

解法一(前缀树)

具体做法是: 弄一个字典树Trie, 然后在Trie中做dfs。 dfs的定义是: bool dfs(string& s, int startPos), 表示在字符串s中开始搜索, 当前的搜索区间为 [startPos, s.size() - 1], 能搜索到就返回true, 否则false。

记忆化: 另外需要用个数组failMemo记录dfs没搜索到时对应的s中的index, 用bool或int数组都可以。

为什么做记忆化?
如果不做记忆化, 部分test case会TLE超时, 因为不做记忆化时会有挺多的重复判断。


class Tire{
public:
    bool isEnd;
    Tire* next[26];
    Tire(){
        isEnd = false;
        for(int i = 0;i < 26;i++){
            next[i] = nullptr;
        }
    }
};

class Solution {
public:
    Tire* root;
    int err_start[301];
    bool wordBreak(string s, vector<string>& wordDict) {
        root = new Tire();
        //初始化前缀树
        for(auto& word : wordDict){
            Tire* p = root;
            for(auto& c : word){
                if(p->next[c-'a'] == nullptr){
                    p->next[c-'a'] = new Tire();
                }
                p = p->next[c-'a'];
            }
            p->isEnd = true;
        }
        return dfs(s,0);
    }
    bool dfs(string s,int start){
        if(err_start[start] == 1) return false;
        if(start == s.size())   return true;
        //在前缀树中寻找
        Tire* p = root;
        for(int i = start;i < s.size();i++){
            if(p->next[s[i] - 'a'] != nullptr){
                p = p->next[s[i] - 'a'];
                //只有该单词到达结尾 下一层dfs可以到达底层 
                if(p->isEnd && dfs(s,i+1)){
                    return true;
                }
            }else break;
        }
        err_start[start] = 1;
        return false;
    }
};

解法二(动态规划)


class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<bool> dp(s.size() + 1, false);
        dp[0] = true;
        for (int i = 1; i <= s.size(); i++) {   // 遍历背包
            for (int j = 0; j < i; j++) {       // 遍历物品
                string word = s.substr(j, i - j); //substr(起始位置,截取的个数)
                if (wordSet.find(word) != wordSet.end() && dp[j]) {
                    dp[i] = true;
                }
            }
        }
        return dp[s.size()];
    }
};

剪枝+优化


class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        const int len = s.size();
        vector<bool> dp(len + 1, false);
        dp[0] = true;
        for (int i = 1; i <= len; i++) {   // 遍历背包
            if(!dp[i-1]) continue;
            for(auto& word:wordDict){
                int j = i - 1 + word.size();
                if(j <= len && s.substr(i - 1,word.size()) == word){
                    dp[j] = true;
                }
            }
        }
        return dp[len];
    }
};
posted @   xiazichengxi  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示
主题色彩