1.题目描述:
给你一个字符串s和一个字符串列表wordDict作为字典。请你判断是否可以利用字典中出现的单词拼接出s。注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
2.动态规划:
这道题实际是代码随想录背包问题模块的最后一题,首先是完全背包,其次与先前的求组合及求排列的背包问题都有所不同,他是求一种特定的排列,因此只能先遍历背包再遍历物品,否则虽然先遍历的物品可以当次重复遍历,但无法在下次遍历出现,也就是示例2输出false。个人感觉代码随想录这里出错了,也有可能是我写不出正确的先遍历物品再遍历背包的代码。以下代码就是示例13正确,示例2错误。状态转移方程就是基于循环增长的字串是否在集合中出现,代码中见。
class Solution {
public boolean wordBreak(String s, List wordDict) {
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 0; i < wordDict.size(); i++) {//先遍历物品
for (int j = wordDict.get(i).length(); j <= s.length(); j++) {//再遍历背包
if (dp[j - wordDict.get(i).length()] &&
wordDict.contains(s.substring(j - wordDict.get(i).length(), j))) dp[j] = true;
}
}
return dp[s.length()];
}
}
先遍历背包再遍历物品(这里的物品也非严格意义上的物品,而是对字符串的划分):
class Solution {
public boolean wordBreak(String s, List wordDict) {
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 0; i <= s.length(); i++) {//先遍历背包
for (int j = 0; j < i; j++) {//再遍历物品
if (wordDict.contains(s.substring(j,i)) && dp[j]) dp[i] = true;
}
}
return dp[s.length()];
}
}
3.记忆化回溯:
class Solution {
public boolean wordBreak(String s, List wordDict) {
Set wordDictSet = new HashSet(wordDict);
int[] memory = new int[s.length()];
return backTrack(s, wordDictSet, 0, memory);
}
public boolean backTrack(String s, Set wordDictSet, int startIndex, int[] memory) {
if (startIndex >= s.length()) return true;
if (memory[startIndex] != 0) return memory[startIndex] == 1 ? true : false;//1表示可以拼出i及以后的字符子串,-1则不能
for (int i = startIndex; i < s.length(); i++) {
String word = s.substring(startIndex, i + 1);
if (wordDictSet.contains(word) && backTrack(s, wordDictSet, i + 1, memory)) {
memory[startIndex] = 1;
return true;
}
}
memory[startIndex] = -1;//走到这里则不能拼出startIndex及以后字符子串
return false;
}
}
二刷回溯:与leetcode140. 单词拆分 II类似,但是超时。
class Solution {
public boolean wordBreak(String s, List wordDict) {
return backTracking(s, wordDict, 0);
}
public boolean backTracking(String s, List wordDict, int index) {
if (index == s.length()) return true;
for (int i = index; i < s.length(); i++) {
String sub = s.substring(index, i + 1);
if (!wordDict.contains(sub)) continue;//剪枝
if (backTracking(s, wordDict, i + 1)) return true;
}
return false;
}
}
回溯加入记忆化:详细解释见leetcode题解。
class Solution {
public boolean wordBreak(String s, List wordDict) {
int[] flag = new int[s.length()];
return backTracking(s, wordDict, 0, flag);
}
public boolean backTracking(String s, List wordDict, int index, int[] flag) {
if (index == s.length()) return true;
if (flag[index] == -1) return false;
for (int i = index; i < s.length(); i++) {
String sub = s.substring(index, i + 1);
if (!wordDict.contains(sub)) continue;//剪枝
if (backTracking(s, wordDict, i + 1, flag)) return true;
}
flag[index] = -1;//走到这里说明当前index后无法拆成功,回溯时再遇到直接返回false
return false;
}
}