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