问题出处:中文版 LeetCode 127题 - 单词接龙 问题
问题描述:(源自LeetCode)
给定两个单词(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" 不在字典中,所以无法进行转换。
解题方法:
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
// 单词字典
Set<String> wordSet = new HashSet<>(wordList);
// 初步判断是否有解
if (!wordSet.contains(endWord)) {
return 0;
}
// 移除开始单词
wordSet.remove(beginWord);
// 待遍历列表(待寻找下级转换列表)
List<WordPattern> ladderWordList = new ArrayList<>(wordSet.size() + 2);
ladderWordList.add(new WordPattern(beginWord, 1));
// 已检查的可转换单词集合
Set<String> checkRepWordSet = new HashSet<>();
// 广度遍历
for (int index = 0; index < ladderWordList.size(); index++) {
WordPattern checkWordPattern = ladderWordList.get(index);
// 初始化可转换单词集合
checkWordPattern.initRepWordSet(wordSet, checkRepWordSet);
for (String repWord : checkWordPattern.repWordSet) {
WordPattern nextWordPattern = new WordPattern(repWord, checkWordPattern.level + 1);
// 是否为目标结尾单词
if (nextWordPattern.word.equals(endWord)) {
return nextWordPattern.level;
}
// 添加至待遍历列表尾部(继续寻找下一级可转换单词)
ladderWordList.add(nextWordPattern);
}
}
return 0;
}
/**
* 封装单词匹配对象
*/
class WordPattern {
/**
* 转换序列位置
*/
int level;
/**
* 原单词
*/
String word;
/**
* 可转换单词集合
*/
Set<String> repWordSet;
WordPattern(String word, int level) {
this.level = level;
this.word = word;
}
/**
* 初始化可转换单词集合:用26个字母依次替换单词的每一个字母
*/
void initRepWordSet(Set<String> wordSet, Set<String> checkedRepWordSet) {
this.repWordSet = new HashSet<>(word.length() * 26);
char[] wordCharArray = word.toCharArray();
for (int index = 0; index < wordCharArray.length; index++) {
char oriChar = wordCharArray[index];
for (char repChar = 'a'; repChar <= 'z'; repChar++) {
// 排除相同字母
if (repChar != oriChar) {
wordCharArray[index] = repChar;
String repWord = new String(wordCharArray);
// 是字典单词 且 未被检查过
if (wordSet.contains(repWord) && !checkedRepWordSet.contains(repWord)) {
repWordSet.add(repWord);
checkedRepWordSet.add(repWord);
}
}
}
wordCharArray[index] = oriChar;
}
}
}
}