problem
Given two words (beginWord and endWord), and a dictionary’s word list, find all shortest transformation sequence(s) 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.
solution
思路过程
- 从一个单词转换到另外一个单词,并且是最短的路径
- 每次只能转换一个字母,有些单词不能一次性转换完成,还有些需要一些单词来中转,我怎么能确定中转呢?怎么来做到最小呢?是不是从不同的位置开始换,结果也会不一样呢?
- 用最暴力的方法,从第一个字母开始,如果能找到替换成endword的中间单词在里面,就直接替换,如果找不到呢?中间可能有好几层替换,,,,
- 不行,可能行太多了,这样下来真的不行
- 放弃,看解答
- 大神的解答就是牛逼,我看都花了好久
方法一:bfs+dfs->单向遍历
代码
Java
class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
Set<String> wordSet = new HashSet<>(wordList);//这里用hashset,一是为了减少重复,二是为了快速查找
List<List<String>> res = new ArrayList<>();
if (!wordSet.contains(endWord) || wordSet.size() == 0) {
return res;
}
Map<String, Set<String>> Make_graphic = new HashMap<>();
//通过bfs找到的都是最短就能到达的
boolean found = bfs(beginWord, endWord, wordSet, Make_graphic);
System.out.print(Make_graphic);
if (found) {
Stack<String> path = new Stack<>();
path.push(beginWord);
dfs(beginWord, endWord, Make_graphic, path, res);
}
return res;
}
public boolean bfs(String beginWord, String endWord, Set<String> wordSet, Map<String, Set<String>> Make_Graphic) {
Queue<String> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
queue.offer(beginWord);
visited.add(beginWord);
boolean found = false;
int WordLength = beginWord.length();
// 这里要用nextLevelVisited来暂时缓存已经被访问过的,因为不这样做,下一层的本可以再访问一次,就访问不了了。比如有两条路径都指向endword,第一跳访问后endword就被访问过了,那么下一条路径就没办法形成了
Set<String> nextLevelVisited=new HashSet<>();
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
String CurrentWord = queue.poll();
char[] charArray = CurrentWord.toCharArray();
for (int j = 0; j < WordLength; j++) {
char originalChar = charArray[j];
for (char k = 'a'; k <= 'z'; k++) {
charArray[j] = k;
if (charArray[j] == originalChar) {
continue;
}
String nextWord = new String(charArray);
if (wordSet.contains(nextWord)) {
if (!visited.contains(nextWord)) {
if (nextWord.equals(endWord)) {
found = true;
}
nextLevelVisited.add(nextWord);
queue.offer(nextWord);
// visited.add(nextWord);
Make_Graphic.computeIfAbsent(CurrentWord, a -> new HashSet<>());
Make_Graphic.get(CurrentWord).add(nextWord);
}
}
}
charArray[j] = originalChar;
}
}
if (found) {
break;
}
visited.addAll(nextLevelVisited);
nextLevelVisited.clear();
}
return found;
}
public void dfs(String BeginWord, String EndWord, Map<String, Set<String>> graphic, Stack<String> path, List<List<String>> res) {
if (BeginWord.equals(EndWord)) {
res.add(new ArrayList<>(path));
}
if (!graphic.containsKey(BeginWord)) {
return;
}
Set<String> NextWord = graphic.get(BeginWord);
for (String next : NextWord) {
path.push(next);
dfs(next, EndWord, graphic, path, res);
path.pop();
}
}
}
方法二:bfs+dfs->双向遍历
对于代码中自己或许可以有些帮助的给了一些注释,
运行结果快了好多好多呀,厉害d=====( ̄▽ ̄*)b
代码
python
class Solution:
def addToGraphic(self, word, changeWord, graphic, forward):
if not forward:
temp = word
word = changeWord
changeWord = temp
if word not in graphic:
graphic[word] = {changeWord}
else:
graphic[word].add(changeWord)
def bfs(self, beginWord, endWord, graphic, wordList):
found = False
forward = True
forward_search = {beginWord}
back_search = {endWord}
visited = {beginWord, endWord}
word_length = len(beginWord)
alpha26 = set('abcdefghijklmnopqrstuvwxyz')
while forward_search and back_search:
if len(forward_search) > len(back_search):
temp = forward_search
forward_search = back_search
back_search = temp
forward = not forward
next_visited = set()
for word in forward_search:
for i in range(word_length):
for j in alpha26:
if j == word[i]:
continue
change_word = word[0:i] + j + word[i + 1:]
if change_word in wordList:
if change_word in back_search:
found = True
self.addToGraphic(word, change_word, graphic, forward)
if change_word not in visited:
next_visited.add(change_word)
self.addToGraphic(word, change_word, graphic, forward)
if found:
return found
visited.update(next_visited)
forward_search = next_visited
return found
def dfs(self, beginword, endword, graphic, res, path):
if beginword == endword:
# 这里如果直接添加,当path改变时,res也会改变,因为指向的是同一块地址,所以res需要添加path重新复制的一块
# res.append(path)
res.append(path.copy())
# print(res,1)
if beginword not in graphic:
return
for word in graphic[beginword]:
# print(path)
path.append(word)
self.dfs(word, endword, graphic, res, path)
path.pop()
def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
if not wordList or endWord not in wordList:
return []
wordList = set(wordList)
res = []
graphic =dict()
# 这里只写了双向查找的
if (self.bfs(beginWord, endWord, graphic, wordList)):
print(graphic)
path = [beginWord]
res=[]
self.dfs(beginWord, endWord, graphic, res, path)
# print(res,2)
return res
return []
Java
class Solution {
public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
Set<String> wordSet = new HashSet<>(wordList);//这里用hashset,一是为了减少重复,二是为了快速查找
List<List<String>> res = new ArrayList<>();
if (!wordSet.contains(endWord) || wordSet.size() == 0) {
return res;
}
Map<String, Set<String>> Make_graphic = new HashMap<>();
//通过bfs找到的都是最短就能到达的,现在要从两个方向来,其实也是形成一样的图,但是,现在就得判断是哪一个方向了
boolean found = bfs(beginWord, endWord, wordSet, Make_graphic);
System.out.print(Make_graphic);
if (found) {
Stack<String> path = new Stack<>();
path.push(beginWord);
dfs(beginWord, endWord, Make_graphic, path, res);
}
return res;
}
public boolean bfs(String beginWord, String endWord, Set<String> wordSet, Map<String, Set<String>> Make_Graphic) {
Set<String> BeginVisted = new HashSet<>();
Set<String> EndVisted = new HashSet<>();
Set<String> visited = new HashSet<>();
BeginVisted.add(beginWord);
EndVisted.add(endWord);
visited.add(beginWord);
visited.add(endWord);
boolean forward=true;
boolean found = false;
int WordLength = beginWord.length();
// 这里要用nextLevelVisited来暂时缓存已经被访问过的,因为不这样做,下一层的本可以再访问一次,就访问不了了。比如有两条路径都指向endword,第一跳访问后endword就被访问过了,那么下一条路径就没办法形成了
while (!BeginVisted.isEmpty() && !EndVisted.isEmpty()) {
Set<String> nextLevelVisited=new HashSet<>();
if(BeginVisted.size()>EndVisted.size()){
Set<String> temp=BeginVisted;
BeginVisted=EndVisted;
EndVisted=temp;
forward=!forward;
}
for (String CurrentWord :BeginVisted) {
char[] charArray = CurrentWord.toCharArray();
for (int j = 0; j < WordLength; j++) {
char originalChar = charArray[j];
for (char k = 'a'; k <= 'z'; k++) {
charArray[j] = k;
// 和原来一样的单词不可以计算
if (charArray[j] == originalChar) {
continue;
}
String nextWord = new String(charArray);
if (wordSet.contains(nextWord)) {
if(EndVisted.contains(nextWord)){
// 这里就是连通了,那么visited那边,一定有了,因为每一层遍历结束过后,才会加入visited
found=true;
addTographic(Make_Graphic,forward,CurrentWord,nextWord);
}
if (!visited.contains(nextWord)) {
nextLevelVisited.add(nextWord);
addTographic(Make_Graphic,forward,CurrentWord,nextWord);
}
}
}
charArray[j] = originalChar;
}
}
if (found) {
break;
}
BeginVisted=nextLevelVisited;
visited.addAll(nextLevelVisited);
}
return found;
}
public void dfs(String BeginWord, String EndWord, Map<String, Set<String>> graphic, Stack<String> path, List<List<String>> res) {
if (BeginWord.equals(EndWord)) {
res.add(new ArrayList<>(path));
}
if (!graphic.containsKey(BeginWord)) {
return;
}
Set<String> NextWord = graphic.get(BeginWord);
for (String next : NextWord) {
path.push(next);
dfs(next, EndWord, graphic, path, res);
path.pop();
}
}
public void addTographic(Map<String,Set<String>> graphic,boolean forward,String currentWord,String nextWord){
if(!forward){
// 是反过来的,就要反着添加
String temp=currentWord;
currentWord=nextWord;
nextWord=temp;
}
graphic.computeIfAbsent(currentWord,a->new HashSet<>());
graphic.get(currentWord).add(nextWord);
}
}
总结
- 死磕真的把一个题搞出来了,看了别人的解答n遍
- 保持刷题·,不然手感好难找回 /(ㄒoㄒ)/~~
- 坚持呀! 冲冲冲~~~ (ง •_•)ง