题目描述

image.png

解题思路

dp

  • 确定dp数组以及下标的含义

dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词

  • 确定递推公式

如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。
所以递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true。
表示截取的上一个字符串已经能被拆分,且在这个区间也出现在字典中,此时才满足0~i能被拆分。

  • 初始化

从递归公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递归的根基,dp[0]一定要为true,否则递归下去后面都都是false了。
那么dp[0]有没有意义呢?
dp[0]表示如果字符串为空的话,说明出现在字典里。

  • 确定遍历顺序

题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。本题不是组合也不是排序,所以先遍历什么都可以。
那么本题使用求排列的方式,还是求组合的方式都可以
即:外层for循环遍历物品,内层for遍历背包 或者 外层for遍历背包,内层for循环遍历物品 都是可以的。
但本题还有特殊性,因为是要求子串,最好是遍历背包放在外循环,将遍历物品放在内循环。
如果要是外层for循环遍历物品,内层for遍历背包,就需要把所有的子串都预先放在一个容器里。

  • 举例推导dp[i]

以输入: s = “leetcode”, wordDict = [“leet”, “code”]为例,dp状态如图:
image.png

  1. public boolean wordBreak(String s, List<String> wordDict) {
  2. // dp数组表示i的背包容量是否可以拆分出单词,dp[i]表示是否
  3. boolean[] dp = new boolean[s.length() + 1];
  4. dp[0] = true;
  5. for (int i = 1; i <= s.length(); i++) { // 先遍历背包
  6. for (int j = 0; j < i; j++) {
  7. String str = s.substring(j, i);
  8. if (wordDict.contains(str) && dp[j] != false) { // 当以前的串可以拆分,并且包含此单词
  9. dp[i] = true;
  10. }
  11. }
  12. }
  13. return dp[s.length()];
  14. }

回溯法

采用记忆回溯法

  1. /**
  2. * 回溯法
  3. *
  4. * @param s
  5. * @param wordDict
  6. * @return
  7. */
  8. public boolean wordBreak(String s, List<String> wordDict) {
  9. set = new HashSet<>(wordDict);
  10. memo = new int[s.length()];
  11. return traversal(s, 0);
  12. }
  13. private Set<String> set;
  14. private int[] memo; // 用来标记startIndex是否被选择过(-1表示从startIndex开始选择不满足)
  15. public boolean traversal(String s, int startIndex) {
  16. // 是否选择到边界
  17. if (startIndex >= s.length()) {
  18. return true;
  19. }
  20. // 如果被选择过,直接返回结果
  21. if (memo[startIndex] == -1) {
  22. return false;
  23. }
  24. for (int i = startIndex; i < s.length(); i++) {
  25. String str = s.substring(startIndex, i + 1);
  26. // 如果单词在字典中,且递归后边的都返回true,则此时字符串满足
  27. if (set.contains(str) && traversal(s, i + 1)) {
  28. return true;
  29. }
  30. }
  31. // 标记
  32. memo[startIndex] = -1;
  33. return false;
  34. }