【一天一大 lee】单词接龙 (难度:中等) - Day20201105

2020-11-11 14:18:44 浏览数 (1)

题目:

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:

  1. 每次转换只能改变一个字母。
  2. 转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回 0。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

示例:

  1. 示例1:
代码语言:javascript复制
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出: 5

解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的长度 5。
  1. 示例2:
代码语言:javascript复制
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

输出: 0

解释: endWord "cog" 不在字典中,所以无法进行转换。

抛砖引玉

思路:

特殊情况: 如果字典中不包含endWord则直接返回0

本题可以从两个角度来思考解法:

  1. 收集wordList中每个单词完成一次转换对应的结果, 再从beginWord中逐个字符尝试替换,直到找到endWord,返回最小的查找次数
  2. 从beginWord开始逐个使用a到z字符替换每个位置的字符,替换的结果在wordList中 则记录替换后的字符和步数, 再将替换后的字符逐个使用a到z字符替换每个位置的字符... 依次类推来加步数 最后返回找到endWord,返回最小步数

广度优先搜索 优化建图

抛砖引玉

  • 声明map记录wordList中每个单词被替换单个字符后对应的子集:*og -> "dog","log","cog"
  • 为了防止重复枚举,声明visitedMap通过哈希记录已经枚举过的单词不在重复参与枚举
代码语言:javascript复制
/**
 * @param {string} beginWord
 * @param {string} endWord
 * @param {string[]} wordList
 * @return {number}
 */
var ladderLength = function(beginWord, endWord, wordList) {
  if (!wordList.includes(endWord)) return 0
  let map = new Map(),
 len = wordList.length;

  // 记录替换字符后的子集
  for (let i = 0; i < len; i  ) {
   const item = wordList[i],
     itemLen = wordList[i].length
    for (let j = 0; j < itemLen; j  ) {
      const newStr = wordList[i].substring(0, j)   '*'   wordList[i].substring(j   1, itemLen);
      if (!map.has(newStr)) map.set(newStr, [])
      map.get(newStr).push(wordList[i])
    }
  }
 // 从 beginWord开始枚举可能替换的结果
  let queue = [[beginWord, 1]],
    visitedMap = new Map([[beginWord, true]]);
  while (queue.length) {
 let [word, level] = queue.shift(),
  itemLen = word.length;
    for (let i = 0; i < itemLen; i  ) {
      const newStr = word.substring(0, i)   '*'   word.substring(i   1, itemLen);
      if (map.has(newStr)) {
        for (let j = 0; j < map.get(newStr).length; j  ) {
    const child = map.get(newStr)[j]
  //   遇到endWord终止
    if (child === endWord) return level   1
  //   避免重复枚举字符
          if (!visitedMap.has(child)) {
            visitedMap.set(child, true)
            queue.push([child, level   1]);
          }
        }
      }
    }
  }
  return 0
};

广度优先搜索 转换单词

  • 题目限定单词只由小写字母组成,那么在转换字符时,只需从beginWord开始, 遍历转换位置逐个替换成a到z的字符就可以枚举所有转换元素,记录每个转换后的元素和转到到其所需步骤。
  • 将转换后的元素再次推送到队列中继续枚举知道遇到endWord返回
代码语言:javascript复制
/**
 * @param {string} beginWord
 * @param {string} endWord
 * @param {string[]} wordList
 * @return {number}
 */
var ladderLength = function(beginWord, endWord, wordList) {
  let wordSet  = new Set(wordList),
        queue = [[beginWord, 1]];
  while (queue.length) {
    let len = queue.length
    for (let i = 0; i < len; i  ) {
      const [word, level] = queue.shift();
      if (word == endWord) return level  1
      for (let i = 0; i < word.length; i  ) {
  //   枚举可能存在的转换
        for (let c = 1; c <= 26; c  ) {
          const newWord = word.slice(0, i)   String.fromCharCode(97 c)   word.slice(i   1)
          if (wordSet.has(newWord)) {
            queue.push([newWord, level   1]);
            wordSet.delete(newWord);
          }
        }
      }
    }
  }
  return 0
};

注意: 上面广度优先搜索方法转换过程存在顺序,队列尾部的元素依赖队列头部元素先转接,则queue需要遵循队列先进先出原则(FIFO)

0 人点赞