LeetCode - Word Break II

题注

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

回到主题,继续做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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值