LeetCode - Word Break II

来源:互联网 发布:天猫品类销售数据 编辑:程序博客网 时间:2024/09/21 09:21

题注

刚才一直在和我的导师进行交流,又学到了很多东西。说实话,现在几位教导过我的老师真是我人生的指明灯,我非常幸运能在高校中遇到这些真正为学生考虑的老师,而且还不止一位!这样也让我更有动力进行学习和研究呀!

回到主题,继续做LeetCode,有了Word Break的基础,正好也挑战一下Word Break II。Word Break II的思路和Word Break很像,但是有一个很重要的问题需要注意。

题目

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"].
A solution is ["cats and dog", "cat sand dog"]

分析

这次不仅仅是返回是否存在Word Break了,连结果也需要一并返回。这很容易看出是一个算法复杂度非常高的问题,举个例子:

s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

dict = ["a", "aa", "aaa", "aaaa"]

大家可以很直观的想想,这结果得有多少个… 因此,在糟糕情况下,这是一个时间和空间复杂度都非常高的问题。

我的想法是延续Word Break的思路。但是,我们要对于每一位都存储可能的组合结果,举个例子:

s = "aaaaaa";

dict = ["a", "aa", "aaa"]

对于s.substring(0,1),有一种结果"a";

对于s.substring(0,2),有两种结果,一种是直接的"aa",一种是从s.substring(0,1)再接上后面的"a",即为“a a”;

对于s.substring(0,3),可以是s.substring(0,1)拼上s.substring(1,3),可以是s.substring(0,2)拼上s.substring(2,3),也可以是s.substring(0,3);

这样思路就清楚了,仍然用动态规划的方法,对于s.substring(0, i),要不为s.substring(0, j) + " " +s.substring(j, i),其中j < i,要不为s.substring(0,i);和前面Word Break结合起来,就得到了最后的结果了。

然而,如果仅仅如此修改,是通过不了的,我首先提交了这样的代码:

public class Solution {    public ArrayList<String> wordBreak(String s, Set<String> dict) {        int length = s.length();          boolean[] can = new boolean[length+1];          ArrayList<String>[] canList = new ArrayList[length+1];        for (int i=1; i <= length; i++){        canList[i] = new ArrayList<String>();        }        can[0] = true;          for (int i = 1; i <= length; i++) {              for (int j = 0; j < i; j++) {                  if (can[j] && dict.contains(s.substring(j, i))) {                      can[i] = true;                      if (j == 0){                    canList[i].add(s.substring(j, i));                    }else{                    for (int k=0; k<canList[j].size(); k++){                    canList[i].add(canList[j].get(k) + " " + s.substring(j, i));                    }                    }                }            }          }          return canList[length];      }}
结果出现了Time Limit Exceeded,其例子为:

s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";

dict = ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"];

然而从我们的分析中,可以知道这样的s和dict一定会出现Time Limit Exceeded的嘛!我试着在Eclipse中运行了一下,代码如下:

public class Solution {    public ArrayList<String> wordBreak(String s, Set<String> dict) {        int length = s.length();          boolean[] can = new boolean[length+1];          ArrayList<String>[] canList = new ArrayList[length+1];        for (int i=1; i <= length; i++){        canList[i] = new ArrayList<String>();        }        can[0] = true;          for (int i = 1; i <= length; i++) {              for (int j = 0; j < i; j++) {                  if (can[j] && dict.contains(s.substring(j, i))) {                      can[i] = true;                      if (j == 0){                    canList[i].add(s.substring(j, i));                    }else{                    for (int k=0; k<canList[j].size(); k++){                    canList[i].add(canList[j].get(k) + " " + s.substring(j, i));                    }                    }                }            }          }          return canList[length];      }    public static void main(String[] args){    String s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";    Set<String> dict = new HashSet<String>();    dict.add("a");    dict.add("aa");    dict.add("aaa");    dict.add("aaaa");    dict.add("aaaaa");    dict.add("aaaaaa");    dict.add("aaaaaaa");    dict.add("aaaaaaaa");    dict.add("aaaaaaaaa");    dict.add("aaaaaaaaaa");    ArrayList<String> result = new Solution().wordBreak(s, dict);    for (int i=0; i<result.size(); i++){    System.out.println(result.get(i));    }    }}
运行一段时间后,结果出现了一个Exception:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOfRange(Unknown Source)
    at java.lang.String.<init>(Unknown Source)
    at java.lang.StringBuilder.toString(Unknown Source)
    at Solution.wordBreak(Solution.java:23)
    at Solution.main(Solution.java:45)

实际上,因为我们对每一个s.substring(0, i)都进行了结果的计算,而这个例子给的又特殊,因此程序直接出现OutOfBound了。可是,仔细一想,都OutOfBound了,那结果还怎么存储并返回的呢?观察一下才知道,原来给的这个例子中,s的末尾是个"b",也就是说这根本没有Word Break!所以我们可以先用Word Break中的代码,在短时间内判断有没有正确的结果,如果有再计算结果。将Word Break的代码增加到Word Break II中,就得到正确的结果了。

代码

public class Solution {    public ArrayList<String> wordBreak(String s, Set<String> dict) {        int length = s.length();          if (!this.isWordBreak(s, dict)){        return new ArrayList<String>();        }        boolean[] can = new boolean[length+1];          ArrayList<String>[] canList = new ArrayList[length+1];        for (int i=1; i <= length; i++){        canList[i] = new ArrayList<String>();        }        can[0] = true;          for (int i = 1; i <= length; i++) {              for (int j = 0; j < i; j++) {                  if (can[j] && dict.contains(s.substring(j, i))) {                      can[i] = true;                      if (j == 0){                    canList[i].add(s.substring(j, i));                    }else{                    for (int k=0; k<canList[j].size(); k++){                    canList[i].add(canList[j].get(k) + " " + s.substring(j, i));                    }                    }                }            }          }          return canList[length];      }        public boolean isWordBreak(String s, Set<String> dict) {    int length = s.length();          boolean[] can = new boolean[length+1];          can[0] = true;          for (int i = 1; i <= length; i++) {              for (int j = 0; j < i; j++) {                  if (can[j] && dict.contains(s.substring(j, i))) {                      can[i] = true;                      break;                  }              }          }          return can[length];     }}


0 0