Bootstrap

【leetcode】139.单词拆分 (DFS+BFS+动态规划图文详解,java实现)

139. 单词拆分

难度中等

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

  • 拆分时可以重复使用字典中的单词。
  • 你可以假设字典中没有重复的单词。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
     注意你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
DFS 思路
  • "leetcode"能否break,可以拆分为:"l"是否是单词表的单词、剩余子串能否break,"le"是否是单词表的单词、剩余子串能否break……
  • 用 DFS 回溯,考察所有的拆分可能,指针从左往右扫描 s 串:
    • 如果指针的左侧部分,是单词表中的单词,则对以指针为开头的剩余子串,递归考察。
    • 如果指针的左侧部分,不是单词表的单词,不用看了,回溯,考察别的分支。

image.png

DFS 代码 超时

通过23/36个用例,遇到这个测试用例超时了:
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab,[“a”,“aa”,“aaa”,“aaaa”,“aaaaa”,“aaaaaa”,“aaaaaaa”,“aaaaaaaa”,“aaaaaaaaa”,“aaaaaaaaaa”]

const wordBreak = (s, wordDict) => {
   
  const wordSet = new Set(wordDict);
  const check = (s, wordSet, start) => {
    // 检查从start开始的子串能否break
    // 指针越界,结束递归
    if (start == s.length) return true;  
    for (let end = start + 1; end <= s.length; end++) {
    
      // end指针划分两部分,word是前缀部分
      const word = s.slice(start, end); 
      // 如果前缀部分是单词表的词,且剩余子串能break,则返回true
      if (wordSet.has(word) && check(s, wordSet, end)) return true;
    }
    // end指针怎么划分都没返回true,则返回false
    return false; 
  };
  return check(s, wordSet, 0);
};
给递归增加记忆化
  • 下面这个例子中,做了大量重复计算:
    image.png
  • 这个递归是前序遍历的,遍历到右侧子树时,前面其实都已经计算过了
  • 用一个数组,去存之前计算的结果,数组索引对应指针位置,值对应子调用的结果。下次遇到相同的子问题,直接返回数组中的缓存值
    image.png
DFS + 记忆化 代码
const wordBreak = (s, wordDict) => {
   
  const wordSet = new Set(wordDict);
  const memo = new Array(s.length);

  const check = (s, wordSet, start, memo) => {
   
    if (start == s.length) return true;
    if (memo[start] !== undefined) return memo[start]; 

    for (let end = start + 1; end <= s.length; end++) {
   
      const word = s.slice(start, end);                
      if (wordSet.has(word) && check(s
;