127.word-ladder


127. 单词接龙

题目地址

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

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

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

示例 1:

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出: 5

解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的长度 5。

示例 2:

输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

输出: 0

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

解法一

bfs

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        Set<String> wordSet = new HashSet<>(wordList);
        if (wordSet.size() == 0 || !wordSet.contains(endWord)) return 0;

        Set<String> visited = new HashSet<>();
        visited.add(beginWord);

        Queue<String> queue =  new LinkedList<>();
        queue.offer(beginWord);

        int step = 1;
        while(!queue.isEmpty()) {

            int curSize = queue.size();
            for(int i = 0; i < curSize; i++) {

                String word = queue.poll();
                char[] wordArray = word.toCharArray();

                for (int j = 0; j < wordArray.length; j++) {

                    char originChar = wordArray[j];

                    for (char k = 'a'; k <= 'z'; k++) {
                        if (wordArray[j] == k) continue;

                        wordArray[j] = k;

                        String nextWord = String.valueOf(wordArray);

                        if (!wordSet.contains(nextWord)) continue;
                        if (nextWord.equals(endWord)) return ++step;
                        if (!visited.contains(nextWord)) {
                            queue.add(nextWord);
                            visited.add(nextWord);
                        }
                    }
                    wordArray[j] = originChar;
                }
            }
            step++;
        }
        return 0;
    }
}
from collections import defaultdict

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
      if not wordList or endWord not in wordList:
        return 0

      L = len(beginWord)
      all_combo_dict = defaultdict(list)
      for word in wordList:
          for i in range(L):
              all_combo_dict[word[:i] + "*" + word[i+1:]].append(word)

      queue = [(beginWord, 1)]
      visited = {beginWord: True}

      while queue:
        current_word, level = queue.pop(0)      
        for i in range(L):
          intermediate_word = current_word[:i] + "*" + current_word[i+1:]
          for word in all_combo_dict[intermediate_word]:
              if word == endWord:
                  return level + 1
              if word not in visited:
                  visited[word] = True
                  queue.append((word, level + 1))
          all_combo_dict[intermediate_word] = []
      return 0

解法二

双向 BFS

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList__) {
        Set<String> wordList = new HashSet<>(wordList__);
        Set<String> beginSet = new HashSet<>();
        Set<String> endSet = new HashSet<>();
        Set<String> visited = new HashSet<>();

        if(wordList.size() == 0 || !wordList.contains(endWord)) return 0;
        int step = 1;

        beginSet.add(beginWord);
        endSet.add(endWord);

        while (!beginSet.isEmpty() && !endSet.isEmpty()) {

            if (beginSet.size() > endSet.size()) {
                Set<String> set = beginSet;
                beginSet = endSet;
                endSet = set;
            }

            Set<String> tmp = new HashSet<>();
            for (String word: beginSet) {
                char[] wordArray = word.toCharArray();
                for (int i = 0; i < wordArray.length; i++) {
                    for (char c = 'a'; c <= 'z'; c++) {
                        char originChar = wordArray[i];
                        if (originChar == c) continue;
                        wordArray[i] = c;
                        String nextWord = String.valueOf(wordArray);

                        if (endSet.contains(nextWord)) {
                            return ++step;
                        }

                        if (!visited.contains(nextWord) && wordList.contains(nextWord)) {
                            tmp.add(nextWord);
                            visited.add(nextWord);
                        }
                        wordArray[i] = originChar;
                    }
                } 
            }
            beginSet = tmp;
            step++;
        }
        return 0;
    }
}
class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        if endWord not in wordList: return 0
        front = {beginWord}
        back = {endWord}

        step = 1;
        wordList = set(wordList)
        wordLen = len(beginWord)

        while front: # while front and back: 这两种写法其实是一样的
            step += 1
            nextFront = set()
            for word in front:
                for i in range(wordLen):
                    for c in [chr(i) for i in range(97,123)]: # import string string.lowercase
                        if c != word[i]:
                            newWord = word[:i] + c + word[i+1:]
                            if newWord in back:
                                return step
                            if newWord in wordList:
                                nextFront.add(newWord)
                                wordList.remove(newWord)
            front = nextFront
            if len(back) < len(front):
                front, back = back, front
        return 0