welcome to my blog
LeetCode Top Interview Questions 127. Word Ladder (Java版; Medium)
题目描述
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest
transformation sequence from beginWord to endWord, such that:
Only one letter can be changed at a time.
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
第一次做; 融合了力扣题解的通用状态哈希表以及LeetCode题解的双向bfs; 核心: 双向bfs并不是同时双向, 实际上是这次从beginWord方向bfs,下次从endWord方向bfs; 代码中实现双向遍历的方式是while循环中的第一个if语句; 代码里没法实现真正的同时双向bfs, 我想了想, 同不同时并不重要, 重要的是从两端进行bfs; 我的感觉是, 仅从beginWord端进行bfs时, 队列中的元素越来越多, 直到队列中出现endWord才停下, 此时队列中包含了大量无关的元素, 如果从两个方向进行bfs, 队列中的无关元素会少一些
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
if(!wordList.contains(endWord))
return 0;
int n = beginWord.length();
HashMap<String,ArrayList<String>>all_commons = new HashMap<>();
wordList.forEach(
word->{
for(int i=0; i<n; i++){
String common = word.substring(0,i)+"*"+word.substring(i+1);
if(!all_commons.containsKey(common))
all_commons.put(common, new ArrayList<String>());
all_commons.get(common).add(word);
}
}
);
HashSet<String> begin = new HashSet<>();
HashSet<String> end = new HashSet<>();
begin.add(beginWord);
end.add(endWord);
HashSet<String> visited = new HashSet<>();
int len = 1;
while(!begin.isEmpty() && !end.isEmpty()){
if(begin.size()>end.size()){
HashSet<String> tmp = begin;
begin = end;
end = tmp;
}
HashSet<String> neighbor = new HashSet<>();
for(String cur : begin){
for(int i=0; i<n; i++){
String tmp = cur.substring(0,i)+"*"+cur.substring(i+1);
if(!all_commons.containsKey(tmp))
continue;
for(String str : all_commons.get(tmp)){
if(end.contains(str))
return len+1;
if(!visited.contains(str)){
visited.add(str);
neighbor.add(str);
}
}
}
}
begin = neighbor;
len++;
}
return 0;
}
}
第一次做; 模仿了LeetCode上的双向bfs; 核心:使用HashSet记录邻居, 遍历完set中的元素后, 让set指向新邻居, 从而实现bfs
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
if(!wordList.contains(endWord))
return 0;
int n = beginWord.length();
HashSet<String> begin = new HashSet<>();
HashSet<String> end = new HashSet<>();
begin.add(beginWord);
end.add(endWord);
HashSet<String> visited = new HashSet<>();
int len = 1;
while(!begin.isEmpty() && !end.isEmpty()){
if(begin.size()>end.size()){
HashSet<String> tmp = begin;
begin = end;
end = tmp;
}
HashSet<String> neighbor = new HashSet<>();
for(String cur : begin){
char[] chs = cur.toCharArray();
for(int i=0; i<n; i++){
char old = chs[i];
for(char ch='a'; ch<='z'; ch++){
chs[i] = ch;
String maybe = String.valueOf(chs);
if(end.contains(maybe))
return len+1;
if(wordList.contains(maybe) && !visited.contains(maybe)){
visited.add(maybe);
neighbor.add(maybe);
}
}
chs[i] = old;
}
}
begin = neighbor;
len++;
}
return 0;
}
}
第一次做; 转成图的问题, 使用宽度优先遍历; 并不是动态规划; jdk1.8的匿名函数写法; javafx需要自行安装; 核心: 通用状态; 什么是相邻? 拥有相同通用状态的两个String互为邻居; 预处理操作在广度优先搜索之前高效的建立了邻接表
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
int n = beginWord.length();
HashMap<String, ArrayList<String>> all_commons = new HashMap<>();
wordList.forEach(
word -> {
for(int i=0; i<n; i++){
String common = word.substring(0,i)+"*"+word.substring(i+1);
ArrayList<String> cur = all_commons.getOrDefault(common, new ArrayList<String>());
cur.add(word);
all_commons.put(common, cur);
}
}
);
HashSet<String> set = new HashSet<>();
LinkedList<Record> queue = new LinkedList<>();
queue.add(new Record(beginWord, 1));
set.add(beginWord);
while(!queue.isEmpty()){
Record r = queue.poll();
String cur = r.word;
set.add(cur);
for(int i=0; i<n; i++){
String tmp = cur.substring(0,i)+"*"+cur.substring(i+1);
if(!all_commons.containsKey(tmp))
continue;
for(String str : all_commons.get(tmp)){
if(str.equals(endWord))
return r.level+1;
if(set.contains(str))
continue;
set.add(str);
queue.add(new Record(str, r.level+1));
}
}
}
return 0;
}
public class Record{
String word;
Integer level;
Record(String word, Integer level){
this.word = word;
this.level = level;
}
}
}
LeetCode题解; 使用HashSet这个结构也能实现bfs, 不一定非得用队列
public class Solution {
public int ladderLength(String beginWord, String endWord, Set<String> wordList) {
Set<String> beginSet = new HashSet<String>(), endSet = new HashSet<String>();
int len = 1;
int strLen = beginWord.length();
HashSet<String> visited = new HashSet<String>();
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> temp = new HashSet<String>();
for (String word : beginSet) {
char[] chs = word.toCharArray();
for (int i = 0; i < chs.length; i++) {
for (char c = 'a'; c <= 'z'; c++) {
char old = chs[i];
chs[i] = c;
String target = String.valueOf(chs);
if (endSet.contains(target)) {
return len + 1;
}
if (!visited.contains(target) && wordList.contains(target)) {
temp.add(target);
visited.add(target);
}
chs[i] = old;
}
}
}
beginSet = temp;
len++;
}
return 0;
}
}
力扣题解
import javafx.util.Pair;
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
int L = beginWord.length();
HashMap<String, ArrayList<String>> allComboDict = new HashMap<String, ArrayList<String>>();
wordList.forEach(
word -> {
for (int i = 0; i < L; i++) {
String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
ArrayList<String> transformations =
allComboDict.getOrDefault(newWord, new ArrayList<String>());
transformations.add(word);
allComboDict.put(newWord, transformations);
}
});
Queue<Pair<String, Integer>> Q = new LinkedList<Pair<String, Integer>>();
Q.add(new Pair(beginWord, 1));
HashMap<String, Boolean> visited = new HashMap<String, Boolean>();
visited.put(beginWord, true);
while (!Q.isEmpty()) {
Pair<String, Integer> node = Q.remove();
String word = node.getKey();
int level = node.getValue();
for (int i = 0; i < L; i++) {
String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
for (String adjacentWord : allComboDict.getOrDefault(newWord, new ArrayList<String>())) {
if (adjacentWord.equals(endWord)) {
return level + 1;
}
if (!visited.containsKey(adjacentWord)) {
visited.put(adjacentWord, true);
Q.add(new Pair(adjacentWord, level + 1));
}
}
}
}
return 0;
}
}
import javafx.util.Pair;
class Solution {
private int L;
private HashMap<String, ArrayList<String>> allComboDict;
Solution() {
this.L = 0;
this.allComboDict = new HashMap<String, ArrayList<String>>();
}
private int visitWordNode(
Queue<Pair<String, Integer>> Q,
HashMap<String, Integer> visited,
HashMap<String, Integer> othersVisited) {
Pair<String, Integer> node = Q.remove();
String word = node.getKey();
int level = node.getValue();
for (int i = 0; i < this.L; i++) {
String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
for (String adjacentWord : this.allComboDict.getOrDefault(newWord, new ArrayList<String>())) {
if (othersVisited.containsKey(adjacentWord)) {
return level + othersVisited.get(adjacentWord);
}
if (!visited.containsKey(adjacentWord)) {
visited.put(adjacentWord, level + 1);
Q.add(new Pair(adjacentWord, level + 1));
}
}
}
return -1;
}
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
if (!wordList.contains(endWord)) {
return 0;
}
this.L = beginWord.length();
wordList.forEach(
word -> {
for (int i = 0; i < L; i++) {
String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
ArrayList<String> transformations =
this.allComboDict.getOrDefault(newWord, new ArrayList<String>());
transformations.add(word);
this.allComboDict.put(newWord, transformations);
}
});
Queue<Pair<String, Integer>> Q_begin = new LinkedList<Pair<String, Integer>>();
Queue<Pair<String, Integer>> Q_end = new LinkedList<Pair<String, Integer>>();
Q_begin.add(new Pair(beginWord, 1));
Q_end.add(new Pair(endWord, 1));
HashMap<String, Integer> visitedBegin = new HashMap<String, Integer>();
HashMap<String, Integer> visitedEnd = new HashMap<String, Integer>();
visitedBegin.put(beginWord, 1);
visitedEnd.put(endWord, 1);
while (!Q_begin.isEmpty() && !Q_end.isEmpty()) {
int ans = visitWordNode(Q_begin, visitedBegin, visitedEnd);
if (ans > -1) {
return ans;
}
ans = visitWordNode(Q_end, visitedEnd, visitedBegin);
if (ans > -1) {
return ans;
}
}
return 0;
}
}