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"].

解答1 (dfs):
让我们来继续切切切吧!

本题与上一题Word Break思路类似,但是一个是DP,一个是DFS。
让我们来回顾一下DP与DFS的区别:
DP是Bottom-up 而DFS是TOP-DOWN.

在本题的DFS中,我们这样定义:
用刀在字符串中切一刀。左边是i个字符,右边是len-i个字符。
i: 1- len
如果:
左边是字典里的词,右边是可以wordbreak的,那么把左边的字符串加到右边算出来的List中,生成新的list返回。
1. Base case:
当输入字符串为空的时候,应该给出一个空解。这个很重要,否则这个递归是不能运行的。
2. 递归的时候,i应该从1开始递归,因为我们要把这个问题分解为2个部分,如果你左边给0,那就是死循环。

记忆:
为了加快DFS的速度,我们应该添加记忆,也就是说,算过的字符串不要再重复计算。举例子:
apple n feng
app len feng
如果存在以上2种划分,那么feng这个字符串会被反复计算,在这里至少计算了2次。我们使用一个Hashmap把对应字符串的解记下来,这样就能避免重复的计算。
否则这一道题目会超时。

 // 我们用DFS来解决这个问题吧
public static List<String> wordBreak1(String s, Set<String> dict) {
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
if (s == null || s.length() == 0 || dict == null) {
return null;
} return dfs(s, dict, map);
} // 解法1:我们用DFS来解决这个问题吧
public static List<String> dfs(String s, Set<String> dict, HashMap<String, List<String>> map) {
if (map.containsKey(s)) {
return map.get(s);
} List<String> list = new ArrayList<String>();
int len = s.length(); if (len == 0) {
list.add("");
} else {
// i 表示左边字符串的长度
for (int i = 1; i <= len; i++) {
String sub = s.substring(0, i); // 左边的子串可以为空,或是在字典内
if (!dict.contains(sub)) {
continue;
} // 字符串划分为2边,计算右边的word break.
List<String> listRight = dfs(s.substring(i, len), dict, map); // 右边不能break的时候,我们跳过.
if (listRight.size() == 0) {
continue;
} // 把左字符串加到右字符串中,形成新的解.
for (String r: listRight) {
StringBuilder sb = new StringBuilder();
sb.append(sub);
if (i != 0 && i != len) {
// 如果左边为空,或是右边为空,不需要贴空格
sb.append(" ");
}
sb.append(r);
list.add(sb.toString());
}
}
} map.put(s, list);
return list;
}

解答2: dfs2:
参考了http://blog.csdn.net/fightforyourdream/article/details/38530983
解法,我们仍然使用主页君用了好多次的递归模板。但是在LeetCode中超时,在进入DFS时加了一个『判断是不是wordBreak』的判断,终于过了。这是一种DFS+剪枝的解法

 /*
// 解法2:我们用普通的递归模板来试一下。
*/ // 我们用DFS来解决这个问题吧
public static List<String> wordBreak(String s, Set<String> dict) {
if (s == null || s.length() == 0 || dict == null) {
return null;
} List<String> ret = new ArrayList<String>(); // 记录切割过程中生成的字母
List<String> path = new ArrayList<String>(); dfs2(s, dict, path, ret, 0); return ret;
} // 我们用DFS模板来解决这个问题吧
public static void dfs2(String s, Set<String> dict,
List<String> path, List<String> ret, int index) {
int len = s.length();
if (index == len) {
// 结束了。index到了末尾
StringBuilder sb = new StringBuilder();
for (String str: path) {
sb.append(str);
sb.append(" ");
}
// remove the last " "
sb.deleteCharAt(sb.length() - 1);
ret.add(sb.toString());
return;
} // 如果不加上这一行会超时。就是说不能break的时候,可以直接返回
// 但这也许只是一个treak, 其实这种方法还是不大好。
if (!iswordBreak(s.substring(index), dict)) {
return;
} for (int i = index; i < len; i++) {
// 注意这些索引的取值。左字符串的长度从0到len
String left = s.substring(index, i + 1);
if (!dict.contains(left)) {
// 如果左字符串不在字典中,不需要继续递归
continue;
} path.add(left);
dfs2(s, dict, path, ret, i + 1);
path.remove(path.size() - 1);
}
} public static boolean iswordBreak(String s, Set<String> dict) {
if (s == null) {
return false;
} int len = s.length();
if (len == 0) {
return true;
} boolean[] D = new boolean[len + 1]; // initiate the DP. 注意,这里设置为true是不得已,因为当我们划分字串为左边为0,右边为n的时候,
// 而右边的n是一个字典string,那么左边必然要设置为true,才能使结果为true。所以空字符串我们需要
// 认为true
D[0] = true; // D[i] 表示i长度的字符串能否被word break.
for (int i = 1; i <= len; i++) {
// 把子串划分为2部分,分别讨论, j 表示左边的字符串的长度
// 成立的条件是:左边可以break, 而右边是一个字典单词
D[i] = false;
for (int j = 0; j < i; j++) {
if (D[j] && dict.contains(s.substring(j, i))) {
// 只要找到任意一个符合条件,我们就可以BREAK; 表示我们检查的
// 这一个子串符合题意
D[i] = true;
break;
}
}
} return D[len];
}

解答3: dfs3:

感谢http://fisherlei.blogspot.com/2013/11/leetcode-wordbreak-ii-solution.html的解释,我们可以加一个boolean的数组,b[i]表示从i到len的的字串可不可以进行word break. 如果我们在当前根本没有找到任何的word, 也就表明这一串是不能word break的,记一个false在数组里。这样下次进入dfs这里的时候,直接就返回一个false.通过这个剪枝我们也可以减少复杂度。

 /*
// 解法3:重新剪枝。
*/
// 我们用DFS来解决这个问题吧
public static List<String> wordBreak3(String s, Set<String> dict) {
if (s == null || s.length() == 0 || dict == null) {
return null;
} List<String> ret = new ArrayList<String>(); // 记录切割过程中生成的字母
List<String> path = new ArrayList<String>(); int len = s.length(); // 注意:一定要分配 Len+1 否则会爆哦.
boolean canBreak[] = new boolean[len + 1];
for (int i = 0; i < len + 1; i++) {
canBreak[i] = true;
} dfs3(s, dict, path, ret, 0, canBreak); return ret;
} // 我们用DFS模板来解决这个问题吧
public static void dfs3(String s, Set<String> dict,
List<String> path, List<String> ret, int index,
boolean canBreak[]) {
int len = s.length();
if (index == len) {
// 结束了。index到了末尾
StringBuilder sb = new StringBuilder();
for (String str: path) {
sb.append(str);
sb.append(" ");
}
// remove the last " "
sb.deleteCharAt(sb.length() - 1);
ret.add(sb.toString());
return;
} // if can't break, we exit directly.
if (!canBreak[index]) {
return;
} for (int i = index; i < len; i++) {
// 注意这些索引的取值。左字符串的长度从0到len
String left = s.substring(index, i + 1);
if (!dict.contains(left) || !canBreak[i + 1]) {
// 如果左字符串不在字典中,不需要继续递归
continue;
} // if can't find any solution, return false, other set it
// to be true;
path.add(left); int beforeChange = ret.size();
dfs3(s, dict, path, ret, i + 1, canBreak);
// 注意这些剪枝的代码. 关键在于此以减少复杂度
if (ret.size() == beforeChange) {
canBreak[i + 1] = false;
}
path.remove(path.size() - 1);
}
}

解答4: DP解法:

感谢大神的解法: https://gist.github.com/anonymous/92e5e613aa7b5ce3d4c5 以后再慢慢研究

主页君自己也写了一个先用动规算出哪些区间是可以解的,然后在DFS的时候,先判断某区间能否word break,如果不可以,直接退出。

     /*
// 解法4:先用DP来求解某些字段是否能word break,然后再做
*/
// 我们用DFS来解决这个问题吧
public static List<String> wordBreak4(String s, Set<String> dict) {
if (s == null || s.length() == 0 || dict == null) {
return null;
} List<String> ret = new ArrayList<String>(); List<String> path = new ArrayList<String>(); int len = s.length(); // i: 表示从i索引开始的字串可以word break.
boolean[] D = new boolean[len + 1];
D[len] = true;
for (int i = len - 1; i >= 0; i--) {
for (int j = i; j <= len - 1; j++) {
// 左边从i 到 j
D[i] = false;
if (D[j + 1] && dict.contains(s.substring(i, j + 1))) {
D[i] = true;
break;
}
}
} dfs4(s, dict, path, ret, 0, D); return ret;
} public static void dfs4(String s, Set<String> dict,
List<String> path, List<String> ret, int index,
boolean canBreak[]) {
int len = s.length();
if (index == len) {
// 结束了。index到了末尾
StringBuilder sb = new StringBuilder();
for (String str: path) {
sb.append(str);
sb.append(" ");
}
// remove the last " "
sb.deleteCharAt(sb.length() - 1);
ret.add(sb.toString());
return;
} // if can't break, we exit directly.
if (!canBreak[index]) {
return;
} for (int i = index; i < len; i++) {
// 注意这些索引的取值。左字符串的长度从0到len
String left = s.substring(index, i + 1);
if (!dict.contains(left)) {
// 如果左字符串不在字典中,不需要继续递归
continue;
} // if can't find any solution, return false, other set it
// to be true;
path.add(left);
dfs4(s, dict, path, ret, i + 1, canBreak);
path.remove(path.size() - 1);
} }

比较与测试:

这里贴一下各种解法的时间:

Test
Computing time with DFS1: 7830.0 millisec.
Computing time with DFS2: 6400.0 millisec.
Computing time with DFS3: 4728.0 millisec.
Computing time with DFS4: 4566.0 millisec.

可见,四个方法里最好的是第四个,建议面试时可以采用第四个。如有错误,敬请指正。

GitHub代码链接

LeetCode: Word Break II 解题报告的更多相关文章

  1. 【LeetCode】140. Word Break II 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归求解 日期 题目地址:https://leetc ...

  2. [LeetCode] Word Break II 解题思路

    Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each ...

  3. LeetCode: Word Ladder II 解题报告

    Word Ladder II Given two words (start and end), and a dictionary, find all shortest transformation s ...

  4. 【LeetCode】Permutations II 解题报告

    [题目] Given a collection of numbers that might contain duplicates, return all possible unique permuta ...

  5. LeetCode: Unique Paths II 解题报告

    Unique Paths II Total Accepted: 31019 Total Submissions: 110866My Submissions Question Solution  Fol ...

  6. LeetCode:Word Break II(DP)

    题目地址:请戳我 这一题在leetcode前面一道题word break 的基础上用数组保存前驱路径,然后在前驱路径上用DFS可以构造所有解.但是要注意的是动态规划中要去掉前一道题的一些约束条件(具体 ...

  7. [leetcode]Word Break II @ Python

    原题地址:https://oj.leetcode.com/problems/word-break-ii/ 题意: Given a string s and a dictionary of words  ...

  8. [LeetCode] Word Break II 拆分词句之二

    Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each ...

  9. LeetCode Word Break II

    原题链接在这里:https://leetcode.com/problems/word-break-ii/ 题目: Given a string s and a dictionary of words  ...

随机推荐

  1. 〖Windows〗Linux的Qt程序源码转换至Windows平台运行,编码的解决

    在中国大陆,Windows默认的编码是gb2312,而Linux是UTF8: 多数情况下,把Linux上的程序转换至Windows上运行需要进行编码转换才能正常显示: 而其实大可以不必的,同样,文件使 ...

  2. HDUOJ----(1016)Prime Ring Problem

    Prime Ring Problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  3. [转]Vue生态系统中的库

    Vue UI组件库 Vuex vux github ui demo:https://github.com/airyland/vux Mint UI 项目主页:http://mint-ui.github ...

  4. word中批量修改图片大小

    一,在word中按alt+f11组合键,进入VBA模式二,在左边的工程资源管理器中找到你的word文档,在其上右键/添加/模块三,把下面代码复制,粘贴进去.四,更改数值, 改一下宽度和高度数值(10) ...

  5. PLSQL常用配置之窗口/版面保存、SQL格式化/美化、SQL注释\去掉注释等快捷键配置、登陆历史修改配置

    http://blog.csdn.net/hyeidolon/article/details/8251791   PLSQL常用配置之窗口/版面保存.SQL格式化/美化.SQL注释\去掉注释等快捷键配 ...

  6. Python len() 方法

    描述 Python len() 方法返回对象(字符串.列表.元组.字典等)长度或项目个数. 语法 len() 方法语法: len(obj) 参数 obj -- 对象(字符串.列表.元组.字典等). 返 ...

  7. C语言訪问MySQL数据库的方法

    1.加入头文件路径(MySQL安装路径中的include路径) 2.加入库文件(直接从MySQL安装路径中copy libmysql.lib就可以) 3.编程操作数据库 代码 // AccessToM ...

  8. linux查看与开启sshd服务

    1.首先通过物理终端进入到linux上,手工检查ssh发现没运行/etc/init.d/sshd statussshd is stopped 手工启动服务,发现报告权限错误./etc/init.d/s ...

  9. 导入sklearn 报错,找不到相关模块

    1.问题原因::安装包的来源问题,也可以理解为包版本兼容问题,有的包使用官方出版,有的包使用whl文件安装解决方案:将所有包都统一来源,要么全部使用官方出版的包,要么全部使用whl里面的包,问题就解决 ...

  10. js判断字符串str是否包含字符串substr

    js判断字符串str是否包含字符串substr: function addUser(id,realName){ var userids = $("#userids").val(); ...