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

15 thoughts on “LeetCode – Word Break II (Java)”

  1. This code will not create the right pos[] for the wordDict = {“cat”, “cats”, “and”, “sand”, “dog”, “at”}

  2. 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…

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

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

    }

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

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

Leave a Comment