问题描述
问题分析
- 该问题是 LeetCode 139. Word Break 的进阶,139是判断能否分割,是一个DP问题(暴力dfs会TLE),而该题是返回分割的具体可能性,是 DFS + 回溯的问题(也有DP的解法,我暂时没看懂…)
- 首先,尝试了暴力DFS,TLE了,因为存在大量的冗余工作,然后关键来了,采用了一种类似于记忆化搜索的套路来对DFS进行剪枝。
noBreeak[i]
: 从i位置开始能否进行break,ture:不能,false:能,初始化全部为false.这样便依旧是一种自顶向下的思路,只不过每次都先对 noBreeak[i]
进行判断,若为true,则直接return,否则,根据是否进行了下一步的dfs来填该值。
- 该题还存在其他解法:Word Break II
经验教训
- 在利用 dfs + 回溯时(求所有具体的可能性,如路径,函数返回值为 void),若提示TLE,尝试用这种 boolean 数组的方法,进行记忆化搜索,从而实现剪枝,优化时间。
代码实现
class Solution {
public List<String> wordBreak(String s, List<String> wordDict) {
HashSet<String> set = new HashSet<>();
int maxLen = 0;
for (String str : wordDict) {
maxLen = Math.max(maxLen, str.length());
set.add(str);
}
char[] chs = s.toCharArray();
List<String> res = new ArrayList<>();
StringBuilder path = new StringBuilder();
boolean[] noBreak = new boolean[chs.length + 1];
dfs(chs, 0, maxLen, set, path, res, noBreak);
return res;
}
public void dfs (char[] chs, int i, int maxLen, HashSet<String> set, StringBuilder path, List<String> res, boolean[] noBreak) {
if (i == chs.length) {
res.add(path.toString());
return;
}
if (noBreak[i] == true) {
return;
}
int pathLen = path.length();
boolean flag = false;
for (int end = i; end < chs.length && end - i + 1 <= maxLen; ++end) {
String newStr = new String(chs, i, end - i + 1);
if (set.contains(newStr) && ! noBreak[end + 1]) {
flag = true;
if (end + 1 == chs.length) {
path.append(newStr);
}else {
path.append(newStr).append(" ");
}
dfs(chs, end + 1, maxLen, set, path, res, noBreak);
path.setLength(pathLen);
}
}
noBreak[i] = flag == false ? true : false;
return;
}
}