zoukankan      html  css  js  c++  java
  • 【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的分隔结果
        }
    };
     
  • 相关阅读:
    C/C++程序员必须熟练应用的开源项目[转]
    VC中基于 Windows 的精确定时[转]
    语音增强算法的概述[转]
    语音增强 [转]
    [转]四款主流手机音乐播放器横评
    1个简单的Log
    [转]全方位对比 安卓手机音乐播放器推荐
    [转]4款手机音乐播放器对比
    一个简单的log
    音频和视频合并
  • 原文地址:https://www.cnblogs.com/wikiwen/p/10229397.html
Copyright © 2011-2022 走看看