【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 a ssume 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的分隔结果
    }
};
 

猜你喜欢

转载自www.cnblogs.com/wikiwen/p/10229397.html