【LeetCode & 剑指offer刷题】动态规划与贪婪法题15:Word Break(系列)

【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)

Word Break(系列)

Word Break
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.
Example 1:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
Example 2:
Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
Example 3:
Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false

C++
 
/*
问题:拆分词句,看给定的词句能分被拆分成字典里面的内容
方法:动态规划
f[i]表示s[0,i-1]是否可以被分词,表示在第i个字符后面的隔板
状态转移方程:f(i) = any_of(f(j)&&s[j,i-1] ∈ dict); j = 0~i-1
例:
Input: s = "leetcode", wordDict = ["leet", "code"]
f[0] = true
i=1,j=0: l
i=2,j=0: le
    j=1: e
i=3,j=0: lee
    j=1: ee
    j=2: e
i=4,j=0: leet  f[4] = true
    j=1: eet
    j=2: et
    j=3: t
...
O(n^2)
假设总共有n个字符串,并且字典是用HashSet来维护,那么总共需要n次迭代,每次迭代需要一个取子串的O(i)操作,然后检测i个子串,而检测是constant操作。所以总的时间复杂度是O(n^2)(i的累加仍然是n^2量级),而空间复杂度则是字符串的数量,即O(n)(本题还需加上字典的空间)
*/
class Solution
{
public:
    bool wordBreak(string s, vector<string> &wordDict)
    {
        unordered_set<string> new_dict(wordDict.begin(), wordDict.end());//转化为哈希表,方便查找
       
        //长度为n的字符串有n+1个隔板,多分配一个空间以方便后续递推
        vector<bool> f(s.size() + 1, false);
       
        f[0] = true; // 空字符串,初始化为true,以便后续迭代
        for (int i = 1; i < f.size(); i++) //以s[i-1]字符结尾的子串, i=1~n
        {
            for (int j = 0; j < i; j++) //以s[j]开头的子串,j=0~i-1
            {
                if (f[j] && new_dict.find(s.substr(j, i-j)) != new_dict.end())//substr[start,len)
                {
                    f[i] = true;
                    break;
                }
            }
        }
        return f[s.size()];
    }
};
 
Word Break II
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.
Note:
  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.
Example 1:
Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
"cats and dog",
"cat sand dog"
]
Example 2:
Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
"pine apple pen apple",
"pineapple pen apple",
"pine applepen apple"
]
Explanation: Note that you are allowed to reuse a dictionary word.
Example 3:
Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]

C++
 
/*
问题:拆分词句2,返回所有分隔结果
方法:dfs
联系排列组合问题
这里用一个hash表避免对相同子串s进行重复分隔,减少重复计算
*/
class Solution {
public:
    vector<string> wordBreak(string s, vector<string>& wordDict)
    {
        unordered_map<string, vector<string>> m;
        return dfs(s, wordDict, m);
    }
    vector<string> dfs(string s, vector<string>& wordDict, unordered_map<string, vector<string>>& m)
    {
        if (m.find(s) != m.end()) return m[s]; //如果对s的分隔已经递归过了,就直接退出
        if (s.empty()) return {""}; //map型数据类型用{},递归的出口
       
        vector<string> res; //某一次的分隔结果
        for (string word : wordDict) //遍历字典中的单词(递归的分支)
        {
            if (word == s.substr(0, word.size()) )//如果当前单词在s开头
            {
                //substr 返回子串 [pos, pos+count) 。若请求的子串越过 string 的结尾,或若 count == npos ,则返回的子串为 [pos, size())
                vector<string> rem = dfs(s.substr(word.size()), wordDict, m); //对该单词后面的子串递归(分支的深度),返回后面子串的分隔结果
               
                for (string str : rem) //拼接后面子串的分隔结果与当前单词
                {
                    res.push_back(word + (str.empty() ? "" : " ") + str); //将word和str push到结果向量中,中间用空格隔开,此为某一种结果
                }               
            }
           
        }
       
        return m[s] = res; //返回对s的分隔结果
    }
};
 
原文地址:https://www.cnblogs.com/wikiwen/p/10229397.html