LeetCode – Word Break II (Java)

Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences. For example, given s = "catsanddog", dict = ["cat", "cats", "and", "sand", "dog"], the solution is ["cats and dog", "cat sand dog"].

Java Solution 1 - Dynamic Programming

This problem is very similar to Word Break. Instead of using a boolean array to track the matched positions, we need to track the actual matched words. Then we can use depth first search to get all the possible paths, i.e., the list of strings.

The following diagram shows the structure of the tracking array.

```public static List<String> wordBreak(String s, Set<String> dict) { //create an array of ArrayList<String> List<String> dp[] = new ArrayList[s.length()+1]; dp[0] = new ArrayList<String>();   for(int i=0; i<s.length(); i++){ if( dp[i] == null ) continue;   for(String word:dict){ int len = word.length(); int end = i+len; if(end > s.length()) continue;   if(s.substring(i,end).equals(word)){ if(dp[end] == null){ dp[end] = new ArrayList<String>(); } dp[end].add(word); } } }   List<String> result = new LinkedList<String>(); if(dp[s.length()] == null) return result;   ArrayList<String> temp = new ArrayList<String>(); dfs(dp, s.length(), result, temp);   return result; }   public static void dfs(List<String> dp[],int end,List<String> result, ArrayList<String> tmp){ if(end <= 0){ String path = tmp.get(tmp.size()-1); for(int i=tmp.size()-2; i>=0; i--){ path += " " + tmp.get(i) ; }   result.add(path); return; }   for(String str : dp[end]){ tmp.add(str); dfs(dp, end-str.length(), result, tmp); tmp.remove(tmp.size()-1); } }```

Java Solution 2 - Simplified

```public List<String> wordBreak(String s, Set<String> wordDict) { ArrayList<String> [] pos = new ArrayList[s.length()+1]; pos[0]=new ArrayList<String>();   for(int i=0; i<s.length(); i++){ if(pos[i]!=null){ for(int j=i+1; j<=s.length(); j++){ String sub = s.substring(i,j); if(wordDict.contains(sub)){ if(pos[j]==null){ ArrayList<String> list = new ArrayList<String>(); list.add(sub); pos[j]=list; }else{ pos[j].add(sub); }   } } } }   if(pos[s.length()]==null){ return new ArrayList<String>(); }else{ ArrayList<String> result = new ArrayList<String>(); dfs(pos, result, "", s.length()); return result; } }   public void dfs(ArrayList<String> [] pos, ArrayList<String> result, String curr, int i){ if(i==0){ result.add(curr.trim()); return; }   for(String s: pos[i]){ String combined = s + " "+ curr; dfs(pos, result, combined, i-s.length()); } }```

This problem is also useful for solving real problems. Assuming you want to analyze the domain names of the top 10k websites. We can use this solution to break the main part of the domain into words and then get a sense of what kinds of websites are popular. I did this a long time ago and found some interesting results. For example, the most frequent words include "news", "tube", "porn", "etc".

Category >> Algorithms >> Interview
If you want someone to read your code, please put the code inside <pre><code> and </code></pre> tags. For example:
```<pre><code>
String foo = "bar";
</code></pre>
```
• Debosmit Ray

There is an issue with type-safety if you use an array of a generic list. Ideally, type-safety is not something you would want to tinker with. A list of ArrayList would be a much better design choice. Working code with couple other fixes: https://github.com/dray92/Programming-Questions/blob/master/leetcode/Word_Break_II.java

• Eugene Arnatovich

Works as magic…
``` public List solution(String s, String[] dict){ int n = s.length(); Map<Integer, List> map = new HashMap(); for (String d : dict) { int m = d.length(); for (int i = 0; i n) { break; } if (s.regionMatches(i, d, 0, m)) { if (!map.containsKey(i)) { map.put(i, new ArrayList()); } map.get(i).add(i+m); } } } StringBuilder sb = new StringBuilder(s); List res = new ArrayList(); func(0, map, sb, res, 0); return res; } public void func(int key, Map<Integer, List> map, StringBuilder sb, List res, int delta) { if (!map.containsKey(key)) { res.add(sb.toString().trim()); return; } sb.insert(key+delta, " "); List lst = map.get(key); for (int k : lst) { func(k, map, sb, res, delta+1); } if (key > 0) { sb.deleteCharAt(key+delta); } }```

• Dexter

How about storing the word start index in t[].

public static int wordBreak(String s, Set dict) {

int[] t = new int[s.length()+1];

//set first to be true, why?

//Because we need initial state

for(int j = 0 ; j < t.length; j++){

t[j] = -1;

}

t[0] = 1;

for(int i=0; i s.length())

continue;

if(t[end] != -1) continue;

if(s.substring(i, end).equals(a)){

t[end] = i;

}

}

}

int start = t[s.length()];

int end = s.length();

while(true){

if(start < 0 || end < 0)

break;

System.out.println(s.substring(start, end));

end = start;

start = t[start];

if(start == 0){

System.out.println(s.substring(start,end));

break;

}

}

return t[s.length()];

}

• Thank you.

• pig

My 90.97% Java Solution in Leetcode with explanation
https://pingzhblog.wordpress.com/2015/09/17/wordbreak-ii/

• BTW, what do you do?

• All right. Thank you.

• Jacob Zhang

Hey dude, your example is right.
But the given input is a Set, which is not a trie. That means it is bound to behave slowly.

• “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa”, [“a”, “aa”, “aaa”, “aaaa”, “aaaaa”, “aaaaaa”, “aaaaaaa”, “aaaaaaaa”]

For this test case, your solution will ETL.
Do you have another solution on this problem?

Thank you.