单词拆分 II

150 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

140. 单词拆分 II - 力扣(LeetCode)

给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。

注意: 词典中的同一个单词可能在分段中被重复使用多次。

示例 1:

输入: s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
输出:["cats and dog","cat sand dog"]

示例 2:

输入: s = "pineapplepenapple", wordDict = ["apple","pen","applepen","pine","pineapple"]
输出: ["pine apple pen apple","pineapple pen apple","pine applepen apple"]
解释: 注意你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
输出:[]

提示:

  • 1 <= s.length <= 20
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 10
  • s 和 wordDict[i] 仅有小写英文字母组成
  • wordDict 中所有字符串都 不同

思路

本题可以用动态规划求解。定义n为字符串s的长度,fn(i)为字符串s[i...n-1]可拆分的单词组,遍历字典,如果s[i...j](i <= j <= n-1)在字典中,fn(i) = s[i...j] + fn(j+1)。初始状态fn(n) = []。为了优化在字典中查找匹配的字符串,需要做一些预处理,我们先把字典排序,再定义一个map,用字典中字符串首字母作为key,存储以key开头的字符串的开始下标和结束下标,查找时,先用s[i]在map中查找开始和结束下标,缩小查找范围。

解题

/**
 * @param {string} s
 * @param {string[]} wordDict
 * @return {string[]}
 */
var wordBreak = function (s, wordDict) {
  wordDict.sort();
  const n = wordDict.length;
  const map = new Map();
  let prev = "";
  let item = [];
  for (let i = 0; i < wordDict.length; i++) {
    let first = wordDict[i][0];
    if (first !== prev) {
      item[1] = i;
      prev = first;
      item = [i, n];
      map.set(first, item);
    }
  }
  const res = [];
  const fn = (i, words) => {
    if (i >= s.length) {
        res.push(words.join(' '))
        return
    }
    let char = s[i];
    if (map.has(char)) {
      const [start, end] = map.get(char);
      const subStr = s.slice(i);
      for (let j = start; j < end; j++) {
        if (subStr.startsWith(wordDict[j])) {
          words.push(wordDict[j]);
          fn(i + wordDict[j].length, words);
          words.pop();
        }
      }
    }
  };
  fn(0, [])
  return res
};