一、单词拆分
元素无重可复选
- base case i==s.length return true,遍历到了最后,
- 因为i+len = s.length,len初始值为1,那么i+1 = s.length,那么i = s.lenth -1 也就是最后一个字符位置
- dp(s,i)函数定义:返回 s[i…] 是否能够被拼出
- 判断字符串S的前缀[0,k]是否存在于WordDict,存在就递归dp(s, i+len)
class Solution {
// 用哈希集合方便快速判断是否存在
HashSet<String> wordDict;
// 备忘录,-1 代表未计算,0 代表无法凑出,1 代表可以凑出
int[] memo;
// 主函数
public boolean wordBreak(String s, List<String> wordDict) {
// 转化为哈希集合,快速判断元素是否存在
this.wordDict = new HashSet<>(wordDict);
// 备忘录初始化为 -1
this.memo = new int[s.length()];
Arrays.fill(memo, -1);
return dp(s, 0);
}
// 定义:s[i..] 是否能够被拼出
boolean dp(String s, int i) {
// base case
if (i == s.length()) {
return true;
}
// 防止冗余计算
if (memo[i] != -1) {
return memo[i] == 0 ? false : true;
}
// 遍历 s[i..] 的所有前缀
for (int len = 1; i + len <= s.length(); len++) {
// 看看哪些前缀存在 wordDict 中
String prefix = s.substring(i, i + len);
if (wordDict.contains(prefix)) {
// 找到一个单词匹配 s[i..i+len)
// 只要 s[i+len..] 可以被拼出,s[i..] 就能被拼出
boolean subProblem = dp(s, i + len);
if (subProblem == true) {
memo[i] = 1;
return true;
}
}
}
// s[i..] 无法被拼出
memo[i] = 0;
return false;
}
}
二、单词拆分2
class Solution {
HashSet<String> wordDict;
// 备忘录
List<String>[] memo;
public List<String> wordBreak(String s, List<String> wordDict) {
this.wordDict = new HashSet<>(wordDict);
memo = new List[s.length()];
return dp(s, 0);
}
// 定义:返回用 wordDict 构成 s[i..] 的所有可能
List<String> dp(String s, int i) {
List<String> res = new LinkedList<>();
if (i == s.length()) {
res.add("");
return res;
}
// 防止冗余计算
if (memo[i] != null) {
return memo[i];
}
// 遍历 s[i..] 的所有前缀
for (int len = 1; i + len <= s.length(); len++) {
// 看看哪些前缀存在 wordDict 中
String prefix = s.substring(i, i + len);
if (wordDict.contains(prefix)) {
// 找到一个单词匹配 s[i..i+len)
List<String> subProblem = dp(s, i + len);
// 构成 s[i+len..] 的所有组合加上 prefix
// 就是构成构成 s[i] 的所有组合
for (String sub : subProblem) {
if (sub.isEmpty()) {
// 防止多余的空格
res.add(prefix);
} else {
res.add(prefix + " " + sub);
}
}
}
}
// 存入备忘录
memo[i] = res;
return res;
}
}