# 140. 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:
[]

# Solution

Approach 1: backtracking -- add a cache to remember what's already calculated.

# Code (Python)

Approach 1:

    def wordBreak(self, s, wordDict):
        words = set(wordDict)
        return list(map(lambda lst: ' '.join(lst), self._break_words(s, words, {})))
    
    def _break_words(self, string, words, cache):
        if string in cache:
            return cache[string]
        if not string or not words:
            return []
        result = []
        for i in range(0, len(string)):
            if string[:i+1] in words:
                if i + 1 == len(string): # base case
                    result.append([string])
                else:
                    for partial_result in self._break_words(string[i+1:], words, cache):
                        result.append([string[:i+1]] + partial_result)
        cache[string] = result
        return result

# Code (C++)

Approach 1:

class Solution {
private:
    unordered_set<string> words;
    unordered_map<string,vector<string>> umap;
    vector<string> res;
    vector<string> doWordBreak(string s, int head) {
        vector<string> res;
        string key = s.substr(head);
        if (umap.find(key) != umap.end())
            return umap[key];
        for (int tail = head; tail < s.size(); ++tail)
        {
            string tmpWord = s.substr(head, tail - head + 1);
            if (words.find(tmpWord) != words.end())
            {
                if (tail == s.size() - 1)
                {
                    res.push_back(tmpWord);
                }
                else
                {
                    vector<string> tmpRes = doWordBreak(s, tail+1);
                    for (int i = 0; i < tmpRes.size(); ++i)
                    {
                        res.push_back(tmpWord + ' ' + tmpRes[i]);
                    }
                }
            }
        }
        umap[key] = res;
        return res;
    }
public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        for (int i = 0; i < wordDict.size(); ++i)
        {
            words.insert(wordDict[i]);
        }
        return doWordBreak(s, 0);
    }
};

// Time Limit Exceeded.
class Solution {
private:
    unordered_set<string> words;
    vector<string> res;
    void doWordBreak(string s, int head, vector<string>& buf) {
        if (head == s.size())
        {
            ostringstream oss;
            oss << buf[0];
            for (int i = 1; i < buf.size(); ++i)
            {
                oss << ' ' << buf[i];
            }
            res.push_back(oss.str());
        }
        for (int tail = head; tail < s.size(); ++tail)
        {
            string tmp = s.substr(head, tail - head + 1);
            if (words.find(tmp) != words.end())
            {
                buf.push_back(tmp);
                doWordBreak(s, tail+1, buf);
                buf.pop_back();
            }
        }
    }
public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        for (int i = 0; i < wordDict.size(); ++i)
        {
            words.insert(wordDict[i]);
        }
        vector<string> buf;
        doWordBreak(s, 0, buf);
        return res;
    }
};