• 139 单词拆分 140 单词拆分II


    题目

    给你一个字符串 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()];//返回结果
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 时间复杂度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();
                }
            }
    
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 时间复杂度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]为单词的情况,如果找到就递归调用,寻找前一个单词。
  • 相关阅读:
    计算机毕业设计ssm+vue基本微信小程序的校园通知小程序系统
    tf.ones_initializer
    Future
    Android Poco初始化时,不大起眼但可能存在坑点的参数们
    C语言力扣第32题之最长有效括号。用栈实现
    DDD/ABP/EF Core :新特性Owned Entity Types ,尝试另外一种值对象的配置方式
    vue.js:哪些数组的方法是响应式的案例
    算法修养--A*寻路算法
    学生身份标签的识别与风控应用
    Java配置24-gitlab分支管理
  • 原文地址:https://blog.csdn.net/weixin_45781228/article/details/126437517