Leetcode: Word Break II

本文探讨了如何使用给定的字符串和词汇表构造有效的句子,并提供了两种方法:直接递归解法和动态规划解法。重点在于解决如何在过程中记录所有合法结果,以及在算法复杂度和空间使用之间的权衡。
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"].

难度:98,参考了别人的思路:这道题目要求跟Word Break比较类似,不过返回的结果不仅要知道能不能break,如果可以还要返回所有合法结果。一般来说这种要求会让动态规划的效果减弱很多,因为我们要在过程中记录下所有的合法结果,中间的操作会使得算法的复杂度不再是动态规划的两层循环,因为每次迭代中还需要不是constant的操作,最终复杂度会主要取决于结果的数量,而且还会占用大量的空间,因为不仅要保存最终结果,包括中间的合法结果也要一一保存,否则后面需要历史信息会取不到。所以这道题目我们介绍两种方法,一种是直接brute force用递归解,另一种是跟Word Break思路类似的动态规划。

类似word break中的DP做法,其实就是在word break的基础上稍作修改:

 1 public class Solution {
 2     public List<String> wordBreak(String s, List<String> wordDict) {
 3         if (s == null || s.length() == 0) return null;
 4         List<List<String>> results = new ArrayList<>();
 5 
 6         HashSet<String> dict = new HashSet<>();
 7         for (String word : wordDict) {
 8             dict.add(word);
 9         }
10         boolean[] res = new boolean[s.length()+1];
11         res[0] = true;
12         results.add(new ArrayList<String>());
13         results.get(0).add("");
14         
15         for (int i=1; i<=s.length(); i++) {
16             results.add(new ArrayList<String>());
17             for (int j=0; j<i; j++) {
18                 if (res[j]) {
19                     String str = s.substring(j, i);
20                     if (dict.contains(str)) {
21                         res[i] = true;
22                         for (String kk : results.get(j)) {
23                             if (kk.length() == 0)
24                                 results.get(i).add(str);
25                             else
26                                 results.get(i).add(kk + " " + str);
27                         }
28                     }
29                 }
30 
31             }
32         }
33         return results.get(s.length());
34     }
35 }

 

还有一点需要指出的是,这个代码放到LeetCode中会MLE,但是面试应该够了,原因是LeetCode中有一个非常tricky的测试case,

Because the existence of test case 'aaaaa...aaaab', ['a', 'aa', 'aaa', ... 'aaa...aa'], the forward DP solution will cause MLE while the backward DP is just fine, apparently the test case 'baaaaaaaa...aaaa', ['a', 'aa', 'aaa', ..., 'aaa...aa'] should also be included.

 

有时间的话,研究一下vote最高的做法, refer to: https://discuss.leetcode.com/topic/27855/my-concise-java-solution-based-on-memorized-dfs

Using DFS directly will lead to TLE, so I just used HashMap to save the previous results to prune duplicated branches, as the following:

 1 public List<String> wordBreak(String s, Set<String> wordDict) {
 2     return DFS(s, wordDict, new HashMap<String, LinkedList<String>>());
 3 }       
 4 
 5 // DFS function returns an array including all substrings derived from s.
 6 List<String> DFS(String s, Set<String> wordDict, HashMap<String, LinkedList<String>>map) {
 7     if (map.containsKey(s)) 
 8         return map.get(s);
 9         
10     LinkedList<String>res = new LinkedList<String>();     
11     if (s.length() == 0) {
12         res.add("");
13         return res;
14     }               
15     for (String word : wordDict) {
16         if (s.startsWith(word)) {
17             List<String>sublist = DFS(s.substring(word.length()), wordDict, map);
18             for (String sub : sublist) 
19                 res.add(word + (sub.isEmpty() ? "" : " ") + sub);               
20         }
21     }       
22     map.put(s, res);
23     return res;
24 }

 

转载于:https://www.cnblogs.com/EdwardLiu/p/3970804.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值