单词接龙
时间:2022-07-22
本文章向大家介绍单词接龙,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
问题描述:
给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:
每次转换只能改变一个字母。 转换后得到的单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回一个空列表。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
示例 2:
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: []
解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-ladder-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解决方案
找beginWord 到 endWord 的最短转换序列问题就相当于找图上两点的最短路径问题。
该问题最简单的解法就是使用dfs把所有的可行路线都找出来,然后再选择其中最短的数条路线。题目只要求最短路线,但是该做法会遍历到所有的路线,做了大量的无用操作,显然会tle。
若只使用bfs获得最短路径,如此虽然避免的许多无用功,但是只能得到满足条件的一条路径,而题目要求返回多条最短路径。
因此我们使用bfs+dfs的方式求解。先使用bfs搜索最短路径时存储最短路径中每个节点的前一个结点的结构。然后从endWord到beginWord该结构进行dfs,列举出所有最短路径。
实现代码如下:
class Solution {
// 先使用bfs得到最短路径中,每个节点的前驱结点
// 再对得到的前驱结点信息使用dfs,得到其路径
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
Set<String> wordSet = new HashSet<>(wordList);
Map<String, List<String>> pre = new HashMap<>();// 存储其前一个结点
Map<String, Boolean> visited = new HashMap<>();
Queue<String> queue = new LinkedList<>();
if(!wordSet.contains(endWord)){
return new ArrayList<>();
}
for(String word : wordList){
pre.put(word, new ArrayList<>());
}
queue.add(beginWord);
while(!queue.isEmpty()){
int len = queue.size();
// 用于存储当前层的下一个结点
Set<String> nexts = new HashSet<>();
for(int i = 0; i < len; i++){
String cur = queue.remove();
char[] arr = cur.toCharArray();
for(int j = 0; j < arr.length; j++){
char temp = arr[j];
for(char c = 'a'; c <= 'z'; c++){
arr[j] = c;
String next = String.valueOf(arr);
if(!wordSet.contains(next) || visited.getOrDefault(next, false)){
continue;
}
pre.get(next).add(cur);
// 先把当前层的next都放入一个Set中,如此为了处理当前层的两个结点到可以直达同一个next
nexts.add(next);
}
arr[j] = temp;
}
}
for(String next : nexts){
visited.put(next, true);
queue.add(next);
}
}
// 使用dfs + 回溯 从尾找头
List<List<String>> result = new ArrayList<>();
if(pre.get(endWord).size() == 0){
return result;
}
dfs(pre, endWord, beginWord, result, new ArrayList<>());
return result;
}
public void dfs(Map<String, List<String>> pre, String cur, String beginWord, List<List<String>> result,
List<String> temp){
if(cur.equals(beginWord)){
temp.add(cur);
List<String> ans = new ArrayList<>(temp);
Collections.reverse(ans);
result.add(ans);
temp.remove(temp.size() - 1);
return;
}
temp.add(cur);
for(String p : pre.get(cur)){
dfs(pre, p, beginWord, result, temp);
}
temp.remove(temp.size() - 1);
}
}
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法