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: []
简单的dfs,能work,但是复杂的case会time out
class Solution {
public List<String> wordBreak(String s, List<String> wordDict) {
List<String> res = new ArrayList<>();
List<String> clist = new ArrayList<>();
Set<String> set = new HashSet<>(wordDict);
helper(s, set, res, clist, 0);
return res;
}
private void helper(String s, Set<String> set, List<String> res, List<String> clist, int start) {
if(start == s.length()){
res.add(convert(clist));
return;
}
for(int i = start; i < s.length(); i++) {
String t = s.substring(start, i+1);
if(set.contains(t)) {
clist.add(t);
helper(s, set, res, clist, i+1);
clist.remove(clist.size() - 1);
}
}
}
private String convert(List<String> clist) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < clist.size() - 1; i++) {
sb.append(clist.get(i) + " ");
}
sb.append(clist.get(clist.size() - 1));
return new String(sb);
}
}
这个方法的逐渐演化
https://leetcode.com/problems/word-break-ii/discuss/44359/Java-recursive-solution-evolution.
This problem looks very easy. First crack let's try simple recursion.
public class Solution {
public List<String> wordBreak(String s, Set<String> wordDict) {
List<String> results = new ArrayList<String>();
_break(s, wordDict, new ArrayList<String>(), results);
return results;
}
private void _break(String s, Set<String> dict, List<String> count, List<String> results){
if (s.equals("")){
StringBuilder b = new StringBuilder();
for (int i=0;i<count.size();i++){
if (i>0 && i<count.size()){
b.append(" ");
}
b.append(count.get(i));
}
results.add(b.toString());
return;
}
for (int i=0;i<s.length();i++){
String sub = s.substring(0, i+1);
if (dict.contains(sub)){
count.add(sub);
_break(s.substring(i+1), dict, count, results);
count.remove(count.size()-1);
}
}
}
}
This passes all tests except TLE on last one. Let's add memoization on cases where we don't get a result (because it's easy to add)
public List<String> wordBreak(String s, Set<String> wordDict) {
List<String> results = new ArrayList<String>();
_break(s, wordDict, new ArrayList<String>(), results);
return results;
}
private Set<String> fails = new HashSet<String>();
private boolean _break(String s, Set<String> dict, List<String> count, List<String> results){
if (s.equals("")){
StringBuilder b = new StringBuilder();
for (int i=0;i<count.size();i++){
if (i>0){
b.append(" ");
}
b.append(count.get(i));
}
results.add(b.toString());
return true;
}
boolean bAll = false;
for (int i=0;i<s.length();i++){
String sub = s.substring(0, i+1);
if (dict.contains(sub) && !(fails.contains(s.substring(i+1)))){
count.add(sub);
boolean b = _break(s.substring(i+1), dict, count, results);
if (b){
bAll = true;
}
else{
fails.add(s.substring(i+1));
}
count.remove(count.size()-1);
}
}
return bAll;
}
Now all test cases pass. But can we do better? Let's add memoization on successes. This is a little trickier as we will need to do a bunch of array copying
public List<String> wordBreak(String s, Set<String> wordDict) {
List<String> r = new ArrayList<String>();
List<List<String>> lists = _break(s, wordDict, new ArrayList<String>(), 0);
for (List<String> list: lists){
StringBuilder b = new StringBuilder();
b.append(list.remove(0));
for (String val: list){
b.append(" ").append(val);
}
r.add(b.toString());
}
return r;
}
private Set<String> fails = new HashSet<String>();
private Map<String, List<List<String>>> sucs = new HashMap<String, List<List<String>>>();
private List<List<String>> _break(String s, Set<String> dict, List<String> count, int index){
List<List<String>> allList = new ArrayList<List<String>>();
if (s.substring(index).equals("")){
allList.add(new LinkedList<String>(count));
}
else {
String sucTest = s.substring(0,index);
if (sucs.containsKey(sucTest)){
List<List<String>> endings = sucs.get(sucTest);
for (List<String> l:endings){
List<String> l1 = new LinkedList<String>(count);
l1.addAll(l);
allList.add(l1);
}
}
else{
for (int i=index;i<s.length();i++){
String sub = s.substring(index, i+1);
String test = s.substring(i+1);
if (dict.contains(sub) && !(fails.contains(test))){
count.add(sub);
List<List<String>> l = _break(s, dict, count, i+1);
if(!l.isEmpty()){
allList.addAll(l);
List<List<String>> copyAll = new LinkedList<List<String>>();
for (List<String> l1:l){
List<String> copy = new LinkedList<String>(l1);
for (String ss:count){
copy.remove(ss);
}
copyAll.add(copy);
}
sucs.put(s.substring(0,i+1), copyAll);
}
else{
fails.add(test);
}
count.remove(count.size()-1);
}
}
}
}
return allList;
}