Word Break Ii
// https://leetcode.com/problems/word-break-ii/
// 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.
// Ex1:
// Input: s = "catsanddog", wordDict = ["cat", "cats", "and", "sand", "dog"]
// Output : ["cats and dog", "cat sand dog"]
// Ex2:
// 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.
// Ex3:
// Input : s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
// Output : []
#include <vector>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <iostream>
#include <cassert>
using namespace std;
// Why we need memorization?
// For example: s = "aabb", dict: ["a", "b", "aa", "bb"]
// In this case "bb" substring will be evaluated twice: once after "a a" and another one after "aa"
// For memorization helps eliminate those duplicate calculations.
// Time complexity without memorization: O(2^n)
// Time complexity with memorization: O(2^n), where n is the length of the string
// we have N^2 substring and for each substring we have to check if it is in the dictionary
// For each list of strings we have to copy it, so it is N^3
// Time: O(2^n), Space: O(n*2^n)
vector<string> dfsWithMemo(string& s, int idx, unordered_set<string>& dict,
unordered_map<int, vector<string>>& cache) {
// Base case
if (idx == s.size()) {
return {""};
}
if (cache.count(idx)) {
return cache[idx];
}
int n = s.size();
vector<string> res;
for (int i = idx; i < n; i++) {
string str = s.substr(idx, i - idx + 1);
if (!dict.count(str)) continue;
vector<string> sub = dfsWithMemo(s, i + 1, dict, cache);
for (string i : sub) {
string newStr = str;
newStr += (i.empty()) ? "" : " " + i;
res.push_back(newStr);
}
}
cache[idx] = res;
return res;
}
vector<string> wordBreak2(string s, vector<string>& dict) {
if (s.empty()) {
return {};
}
unordered_set<string> d;
for (string s : dict) {
d.insert(s);
}
unordered_map<int, vector<string>> cache;
return dfsWithMemo(s, 0, d, cache);
}
void dfs(string& s, int idx, unordered_set<string>& dict,
vector<vector<string>>& res,
vector<string>& curr) {
// Base case
if (idx == s.size()) {
res.push_back(curr);
return;
}
int n = s.size();
for (int i = idx; i < n; i++) {
string str = s.substr(idx, i-idx+1);
if (!dict.count(str)) continue;
curr.push_back(str);
dfs(s, i+1, dict, res, curr);
curr.pop_back();
}
}
vector<string> wordBreak(string s, vector<string>& dict) {
if (s.empty()) {
return {};
}
unordered_set<string> d;
vector<vector<string>> res;
vector<string> curr;
for (string s : dict) {
d.insert(s);
}
dfs(s, 0, d, res, curr);
vector<string> output;
for (vector<string>& v : res) {
string curr = "";
for (int i = 0; i < v.size(); i++) {
if (!curr.empty()) curr += " ";
curr += v[i];
}
output.push_back(curr);
}
return output;
}
int main() {
vector<string> dict1 = {"cat", "cats", "and", "sand", "dog"};
vector<string> res1 = {"cats and dog", "cat sand dog"};
vector<string> output1 = wordBreak2("catsanddog", dict1);
for (string i : output1) cout << i << endl;
// assert(wordBreak("catsanddog", dict1) == res1);
vector<string> dict2 = {"apple", "pen", "applepen", "pine", "pineapple"};
vector<string> res2 = {"pine apple pen apple", "pineapple pen apple", "pine applepen apple"};
vector<string> output2 = wordBreak2("pineapplepenapple", dict2);
for (string i : output2) cout << i << endl;
vector<string> dict3 = {"cats", "dog", "sand", "and", "cat"};
vector<string> res3 = {};
assert(wordBreak2("catsandog", dict3) == res3);
return 0;
}```
Last updated