Word Break II
Problem Statementā
Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences in any order.
Note that the same word in the dictionary may be reused multiple times in the segmentation.
Examples:
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: []
Constraints:
1 <= s.length <= 201 <= wordDict.length <= 10001 <= wordDict[i].length <= 10sandwordDict[i]consist of only lowercase English letters.- All the strings of
wordDictare unique. - Input is generated in a way that the length of the answer doesn't exceed
105.
Solutionsā
Approachā
To solve the problem of generating all possible sentences from s using words from wordDict, we can utilize a recursive backtracking approach combined with memoization for efficiency.
-
Recursive Backtracking with Memoization:
- Use a recursive function
wordBreakHelperto explore all possible segmentations ofs. - Use a
SetforwordDictto achieve O(1) time complexity for word lookups. - Maintain a
Map(memo) to store results of already computed segmentations to avoid redundant computations.
- Use a recursive function
-
Base Case and Recursive Case:
- Base Case: When
index == s.length(), if the current segmentation (temp) is empty ("".equals(temp)), add the segmented string fromsbto the result list (list). - Recursive Case: For each character in
sstarting fromindex, form a substring (temp) and check if it exists inwordDict. If it does, add it tosband recursively callwordBreakHelperwith updatedindexandtemp. After recursion, backtrack by removingtempfromsb.
- Base Case: When
-
Memoization:
- Before making a recursive call, check if the current
indexandsb.toString()combination already exists inmemo. If it does, directly retrieve the result frommemoto avoid recomputation.
- Before making a recursive call, check if the current
Code(C++):ā
#include <vector>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <sstream>
using namespace std;
class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
unordered_map<int, vector<string>> memo;
return wordBreakHelper(s, wordSet, 0, memo);
}
private:
vector<string> wordBreakHelper(const string& s, const unordered_set<string>& wordDict, int start, unordered_map<int, vector<string>>& memo) {
if (memo.find(start) != memo.end()) {
return memo[start];
}
vector<string> result;
if (start == s.length()) {
result.push_back("");
return result;
}
for (int end = start + 1; end <= s.length(); ++end) {
string word = s.substr(start, end - start);
if (wordDict.find(word) != wordDict.end()) {
vector<string> sublist = wordBreakHelper(s, wordDict, end, memo);
for (const string& sub : sublist) {
result.push_back(word + (sub.empty() ? "" : " ") + sub);
}
}
}
memo[start] = result;
return result;
}
};
Code (Java):ā
class Solution {
List<String> list = new ArrayList<>();
StringBuilder sb = new StringBuilder();
Map<String, List<String>> memo = new HashMap<>();
public List<String> wordBreak(String s, List<String> wordDict) {
Set<String> wordSet = Set.copyOf(wordDict);
wordBreakHelper(s, wordSet, 0);
return list;
}
private List<String> wordBreakHelper(String s, Set<String> wordDict, int index) {
if (index == s.length()) {
List<String> temp = new ArrayList<>();
if ("".equals(sb.toString())) {
temp.add(sb.substring(0, sb.length() - 1).toString());
}
return temp;
}
String current = s.substring(index);
if (memo.containsKey(current)) {
return memo.get(current);
}
List<String> sentences = new ArrayList<>();
StringBuilder temp = new StringBuilder();
for (int i = index; i < s.length(); i++) {
temp.append(s.charAt(i));
if (wordDict.contains(temp.toString())) {
int len = sb.length();
sb.append(temp).append(' ');
List<String> nextSentences = wordBreakHelper(s, wordDict, i + 1);
for (String sentence : nextSentences) {
sentences.add(sb.toString() + sentence);
}
sb.setLength(len); // Backtrack
}
}
memo.put(current, sentences);
return sentences;
}
Code (Python):ā
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
self.result = []
self.sb = []
def wordBreakHelper(index):
if index == len(s):
self.result.append(' '.join(self.sb))
return
temp = []
for i in range(index, len(s)):
temp.append(s[i])
if ''.join(temp) in wordDict:
self.sb.append(''.join(temp))
wordBreakHelper(i + 1)
self.sb.pop()
wordBreakHelper(0)
return self.result
Complexity Analysisā
- Time Complexity: , in the worst case, where
nis the length of the strings. - Space Complexity: , in the worst case, where
nis the length of the strings.