LeetCode:139.单词拆分I、II

139. 单词拆分

给定一个非空字符串 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
  • 思路

    ​ 因为说明中告诉我们可以重复使用字典中的单词,所以我们只需要关注字符串s的状态即可。

    ​ 可以通过DP一维数组维护前i串是否恰好可以被分成字典中出现的单词。
    d p [ i ] = d p [ i − l e n ] & & ( s u f f e r [ i − l e n + 1 ] = = w o r d D i c t [ j ] ) dp[i]=dp[i-len] \&\& (suffer[i-len+1]==wordDict[j]) dp[i]=dp[ilen]&&(suffer[ilen+1]==wordDict[j])
    ​ suffer数组记录前缀字符串,可以直接打表实现。

  • 代码

    class Solution {
    public:
        bool dp[201];
        bool wordBreak(string s, vector<string>& wordDict) {
            int n=wordDict.size();
            string ss[201][201];
            //记录各个区间下的字符串
            for(int i=0;i<s.size();i++){
                string q = "";
                for(int j=i;j<s.size();j++){
                    q += s[j];
                    ss[i][j] = q;
                }
            }
            for(int i=0;i<s.size();i++){
                for(int j=0;j<n;j++){
                    int len = wordDict[j].size();
                    if(i>=len-1){
                        if(ss[i-len+1][i]==wordDict[j]){
                            if(i-len<0) dp[i] = true;
                            else dp[i] = dp[i-len];
                        }
                        if(dp[i]) break;//当前成立时直接退出即可。
                    }
                }
            }
            return dp[s.size()-1];
        }
    };
    
进阶:
140. 单词拆分 II

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

说明:

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

示例 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"
]
解释: 注意你可以重复使用字典中的单词。
  • 思路

    ​ 思路与第一题差不多,但这里我们需要记录所有的可能情况,那可以确定需要使用回溯法解决。

    ​ 这里利用DFS从后往前依次查找前缀字符串,并记录即可。

    ​ 其中前缀字符串与字典中的word匹配可用集合优化。

  • 代码

    class Solution {
    public:
        bool dp[201];
        string ss[201][201];
        vector<string> v;
        set<string> word;
        vector<string> wordBreak(string s, vector<string>& wordDict) {
            int n=wordDict.size();
            //记录各个区间下的字符串
            for(int i=0;i<s.size();i++){
                string q = "";
                for(int j=i;j<s.size();j++){
                    q += s[j];
                    ss[i][j] = q;
                }
            }
            for(int i=0;i<n;i++){
                word.insert(wordDict[i]);
            }
            for(int i=0;i<s.size();i++){
                for(int j=0;j<n;j++){
                    int len = wordDict[j].size();
                    if(i>=len-1){
                        if(ss[i-len+1][i]==wordDict[j]){
                            if(i-len<0) dp[i] = true;
                            else dp[i] = dp[i-len];
                        }
                        if(dp[i]) break;//当前成立时直接退出即可。
                    }
                }
            }
            
            if(dp[s.size()-1]){
                dfs(s.size()-1,"");
            }
            return v;
        }
        void dfs(int x,string ans){//x为当前位置,ans记录匹配符合的字符串
            if(x<0) {
                v.push_back(ans);
                return;
            }
            for(int i=x;i>=0;i--){
                if(word.count(ss[i][x])){
                    if(ans=="") dfs(i-1,ss[i][x]);
                    else dfs(i-1,ss[i][x]+" "+ans);
                }
            }
        }
    
    };
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值