两题均可以视为简单图的最短路径(字典里单词表示图的节点,两个只有一个字母差别的单词用图的边表示)
(一)word ladder
https://leetcode.com/problems/word-ladder/description/
题目:给出起始两个单词和一个字典,每次只能变化一个字母,且变化后的单词必须存在于字典当中。要求返回变换成功的最少变换次数。
解答:(BFS)将起始单词放入队列,建立一个hashset存放已经变换过的单词(避免重复);
若队列不空,每次将队列现有的所有节点都取出。对于每个取出的节点,遍历其所有可能的变换:若变换不存在于hashset中,则将其放入hashset中并加入队列。
第一次犯错:比较两个string是否相等, 不能用==,应该使用 a.equals(b);
第二次犯错:queue.size() 不可直接放入循环体中,因为queue在不断变化。应该先将queue.size()赋值给一个常量size,再将size放入循环体中
代码
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
if (beginWord.equals(endWord)) return 1;
int res = 1;
Set<String> dict = new HashSet<>();
for (String word : wordList) {
dict.add(word);
}
Set<String> usedWord = new HashSet<>();
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
usedWord.add(beginWord) ;
while(!queue.isEmpty()) {
res++;
int size = queue.size();
for (int i = 0; i < size; i++) {
String node = queue.poll();
for (String nextWord : nextWords(node, dict)) {
if (nextWord.equals(endWord)) {
return res;
}
if(usedWord.contains(nextWord)) {
continue;
}
queue.offer(nextWord);
usedWord.add(nextWord);
}
}
}
return 0;
}
private List<String> nextWords(String word, Set<String> dict) {
List<String> res = new ArrayList<>();
for (int i = 0; i < word.length(); i++) {
for (char c = 'a'; c <= 'z'; c++) {
if (dict.contains(replacedWord(word, i, c))) {
res.add(replacedWord(word, i, c));
}
}
}
return res;
}
private String replacedWord(String word, int index, char c) {
String res = "";
for (int i = 0; i < word.length(); i++) {
if (i == index) {
res += c;
} else {
res += word.charAt(i);
}
}
return res;
}
}
(二)word ladder II
题目:给出起始两个单词和一个字典,每次只能变化一个字母,且变化后的单词必须存在于字典当中。要求返回所有变换成功的最短路径。
解答:先使用bfs将所有点和起点的距离存储在map中并返回最短路径的长度;
使用dfs从头开始遍历(只遍历距离越来越远的点),若遍历深度大于最短路径,则停止遍历;
第一次犯错:效率太低(在bfs的同时,把nextWords用map存储好,在dfs时无需再次调用nextWords函数,直接从map中获取);
代码:
class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
List<List<String>> res = new ArrayList<>();
Map<String, Integer> distMap = new HashMap<>();
Map<String, List<String>> nextWords = new HashMap<>();
Set<String> set = new HashSet<>();
List<String> singleList = new ArrayList<>();
singleList.add(beginWord);
for (String word : wordList) {
set.add(word);
}
int shortestDist = bfsMethod(beginWord, endWord, distMap, set, nextWords);
if (shortestDist == -1) {
return res;
}
dfsMethod(beginWord, endWord, distMap, nextWords, res, shortestDist, singleList);
return res;
}
private int bfsMethod(String beginWord, String endWord, Map<String, Integer> distMap, Set<String> set, Map<String, List<String>> nextWords) {
int res = 0;
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
int distance = 0;
distMap.put(beginWord, 0);
nextWords.put(beginWord, new ArrayList<String>());
for (String s : set) {
nextWords.put(s, new ArrayList<String>());
}
while (!queue.isEmpty()) {
int size = queue.size();
distance++;
for (int i = 0; i < size; i ++) {
String node = queue.poll();
for (String nextWord : nextWords(node, set)) {
nextWords.get(node).add(nextWord);
if (nextWord.equals(endWord)) {
res = distance;
}
if (!distMap.containsKey(nextWord)) {
distMap.put(nextWord, distance);
queue.offer(nextWord);
}
}
}
}
return res;
}
private void dfsMethod(String beginWord, String endWord, Map<String, Integer> distMap, Map<String, List<String>> nextWords, List<List<String>> list, int maxDist, List<String> singleList) {
for (String nextWord : nextWords.get(beginWord)) {
if (maxDist <= 0) {
continue;
}
if (distMap.containsKey(nextWord) && distMap.get(nextWord) <= distMap.get(beginWord)) {
continue;
}
singleList.add(nextWord);
if (nextWord.equals(endWord)) {
list.add(new ArrayList<>(singleList));
} else {
dfsMethod(nextWord, endWord, distMap, nextWords, list, maxDist - 1,singleList);
}
singleList.remove(singleList.size() - 1);
}
}
private List<String> nextWords(String word, Set<String> set) {
List<String> res = new ArrayList<String>();
for (int i = 0; i < word.length(); i++) {
for (char ch = 'a'; ch <= 'z'; ch++) {
if (ch != word.charAt(i)) {
String s = word.substring(0, i) + ch
+ word.substring(i + 1);
if (set.contains(s)) {
res.add(s);
}
}
}
}
return res;
}
}