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