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.

word-break-II-java

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>

  1. antonio081014 on 2015-7-12

    “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.

  2. Jacob Zhang on 2015-7-18

    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.

  3. antonio081014 on 2015-7-18

    All right. Thank you.

  4. antonio081014 on 2015-7-18

    BTW, what do you do?

  5. pig on 2015-9-16

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

  6. antonio081014 on 2015-9-18

    Thank you.

  7. Dexter on 2015-10-18

    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()];

    }

  8. Eugene Arnatovich on 2016-3-6

    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);
    }
    }

  9. Debosmit Ray on 2016-4-28

    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

  10. HG on 2017-3-5

    whats your algo ?

  11. Yauhen Arnatovich on 2017-3-5

    Clarify your question ?

  12. Yauhen Arnatovich on 2017-3-6

    hi mate, I am sorry, but your question does not make sense. Why ? Showing me existing solutions to this problem which are similar to mine does not make my solution to be not mine. You may think I posted kind of “modified” version of existing ones. It is up to you. Now, you ask yourself, How would I benefit at all from posting this solution, which you think it is “modified” version of existing ones ? Or, may be you composed very similar algo to mine and now you think that this is actually yours ? 🙂 What drives you this way ?
    Btw, can you confirm that my algo matches with every single line of existing ones ? 🙂 If so, I promise I will remove my post.

    Furthermore, it is a classic problem which has multiple ways to solve. And, I do not think you will find completely “unique” solution which totally differs from existing ones. Will you ? Or, you come out with your “original” solution and post it. Then, you can proudly say (according to your logic): “… It is MY algo”.

    Just advice, instead of asking such non-sense questions, you may try to gather all peoples’ thoughts, analyse algos and summarize them. It will help you for your purpose more, rather than you are trying to find out a source of originality, which is in turn pointless as we are all here to learn rather than trying to prove something to someone…

  13. German Attanasio Ruiz on 2017-3-12

    Dictionary is a Set that means O(1) for insert and O(1) for lookup. Here is what I did in Javascript http://codepen.io/germanattanasio/pen/MpmRzV

Leave a comment

*