Leetcode 127. Word Ladder I & 126. Word Ladder II
来源:互联网 发布:文字录入 网络兼职 编辑:程序博客网 时间:2024/05/17 09:25
连做带debug花了5个多小时,刷新了LRU Cache的记录。。
127. Word Ladder
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 intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
Note:
- Return 0 if there is no such transformation sequence.
- All words have the same length.
- All words contain only lowercase alphabetic characters.
参考的blog地址:
https://cheonhyangzhang.wordpress.com/2015/10/01/127-leetcode-java-word-ladder-medium/
http://www.cnblogs.com/tonyluis/p/4532839.html
http://siukwan.sinaapp.com/?p=995
第一个地址双linkedlist解题,第二个是hashmap+linkedlist,两者当然是第一个快,第三个主要刚开始用来看解析的。
一开始看到这题,这不是当年某本书图论中的相似问题吗,只不过伪装了一下,现在变成了String,可以新建一个表,表示点(一个String)点之间的连通情况,
因为是无向图,于是邻接矩阵一定是对称的,于是问题就变成了:给一个邻接矩阵,求两点之间的最短路径。于是第一步先要产生表格,O(n^2)时间和空间;然后还要BFS。。想到这里,楼主就开始search了。。觉得这么琐碎(菜鸟都这样,因为不熟练觉得太琐碎,大牛看到啪啪啪30分钟打完了代码)。
于是搜索到了第三个blog一看,果然OJ会超时。。(吐槽一下mathwork那string reverse题目同样代码弄到OJ上改了半个多小时。。果然是专门盯着非法输入编写的test case啊!!)于是看了人家思路。现在word ladder II 博主也终于全部自己写了一遍,虽然百分比极低,但是作为全部手打,ecplise debug两小时的劳动成果,还是非常高兴!!扯远了。。
既然无法建立表,那么就换一种逻辑。。也真心巧妙,步骤如下:(shortest path当然是bfs,保证碰到的时候一定是最短的)
1. 取一个词,然后对其每一位,换成a~z,check是否在wordlist中,如果在说明可以到达,将其入队(java 中用linkedlist模拟)。trick是:入队之后,便从wordlist中将该match删去,这样就避免了重复case。(赞!)
2. 对其每一位都check之后,目前queue中的单词便是所有变化一次的单词了!下次自然是取一个元素再次进行1。当然1中没说的是如果变化后的单词有match给的target单词,那么就该返回长度,或者level数了。那么问题来了,怎么记录level数。
3.先看到的其实是hashmap的方法,队中是下个level的点,而map就负责存储入队时的level数,这样match的时候取出存储的Integer值就行了,至于+1还是直接返回,要看具体程序怎么写的,改进是扔掉hashmap,采用linkedlist做的另外一个queue,这样俩queue就好比一个hashmap,但是比hashmap灵活,
因为我们既要取出头一个点,又要取出对应的值,前者的操作占了绝大多数,所以还是list更符合要求。这样的话,一个点入queue时,其对应的level数也入另外一个队,这样同存同取即可。
这里有个大bug,博主碰到debug很久。一开始想偷懒,说你们为啥一个个都用俩结构,一个queue不行吗,于是写了个程序,每当queue出货的时候,更新level++,这样很明显错了比如hot可以到dot和lot,dot和lot同属于一级,那它俩应该对应一个level值,ok cool,改进成queue空的时候level++可以吗?NO。比如我取出了dot,发现dot可以变成dog,于是按照上面的逻辑,dog又入队了,这样level一直不增加,dog本来应该是增加的,就少了。
简单来说,就是一个queue是无法建立对应的关系的,要么用HashMap,直接保存了level信息,要么用双list,也保存了对应信息。重点就是对应!因为无法知道什么时候改变,所以只能存了,取出来一看是多少就是多少+1。举得例子都是卡住的case,若你也卡住了很久,不妨看看这两段话。
解法一:HashMap+LinkedList (as Queue)
public class Solution { //109ms public int ladderLength(String beginWord, String endWord, Set<String> wordList) { LinkedList<String> queue = new LinkedList<String>(); HashMap<String, Integer> hm = new HashMap<String, Integer>(); queue.add(beginWord); wordList.remove(beginWord); int length=1; hm.put(beginWord,length); while(queue.size()>0){ String first = queue.removeFirst(); int curlength = hm.get(first)+1; char[] cur = first.toCharArray(); for(int i=0; i<cur.length; i++){ char tempchar = cur[i]; for (int j=0; j<26; j++){ if (tempchar=='a'+j) continue; cur[i]=(char)('a'+j); String temp = new String(cur); ////////////////////////////// if (temp.equals(endWord)) { return curlength; } if (wordList.contains(temp)){ wordList.remove(temp); queue.add(temp); hm.put(temp,curlength); } } cur[i] = tempchar; } } return 0;}}解法二:双LinkedList
public class Solution { public int ladderLength(String beginWord, String endWord, Set<String> wordList) { LinkedList<String> queue = new LinkedList<String>(); LinkedList<Integer> len = new LinkedList<Integer>(); queue.add(beginWord); wordList.remove(beginWord); len.add(1); while(queue.size()>0){ String first = queue.removeFirst(); int curlength = len.removeFirst(); char[] cur = first.toCharArray(); for(int i=0; i<cur.length; i++){ char tempchar = cur[i]; for (int j=0; j<26; j++){ if (tempchar=='a'+j) continue; cur[i]=(char)('a'+j); String temp = new String(cur); ////////////////////////////// if (temp.equals(endWord)) { return curlength+1; } if (wordList.contains(temp)){ wordList.remove(temp); queue.add(temp); len.add(curlength+1); } } cur[i] = tempchar; } } return 0;}}同解法大同小异的另外一版,这题非常繁琐,debug疯掉,所以code不美观,客官见谅。
public class Solution { public int ladderLength(String beginWord, String endWord, Set<String> wordDict) { LinkedList<String> queue = new LinkedList<String>(); LinkedList<Integer> level = new LinkedList<Integer>(); if (beginWord == null || endWord == null) return 0; queue.add(beginWord); level.add(1); wordDict.remove(beginWord); while(queue.size()>0){ String temp = queue.removeFirst(); int lvl = level.removeFirst(); char[] cur = temp.toCharArray(); for(int i=0; i<cur.length; i++){ char tempchar = cur[i]; for (int j=0; j<26; j++){ if (tempchar=='a'+j) continue; cur[i]=(char)('a'+j); String curString = new String(cur); if(curString.equals(endWord)){ return lvl+1; } if(wordDict.contains(curString)){ wordDict.remove(curString); queue.add(curString); level.add(lvl+1); } } cur[i] = tempchar; } } return 0; }}
126. Word Ladder II
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 intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Return
[ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
Note:
- All words have the same length.
- All words contain only lowercase alphabetic characters.
刚一看到,卧槽,长度不满足了,还要路径?
这题繁琐的地方在于,无法正常backtracking,以为前面一题的解法都是iterative的,backtracking的要点就是递归。当然网上是有递归版本,但他们都有建立自己的数据结构,从而满足递归的要求。大神轻拍,做了5个小时就自己目前理解是这样。BFS先算一遍,知道长度,然后DFS递归。。还要套backtracking的方法。。怪不得做完才知道AC率很低,trivial的题。当然还是强行backtracking了,看下面。
自己思路如下:
一开始上个题目算出了最短路径长度,返回了int;那么现在我完全可以让它返回一个List<List<String>>,也就是上一个题中的每一层的信息levels,比如我们start的单词在该List的0 index出,变化一次的单词存在 1 index处,依次类推。这样假如上个题的返回值是5,那么现在这个levels中应该有4个元素,包括start,和end前一步的单词。
有了这个levels就好办了。就变成了,有目标单词,有起始单词,有intermediate单词。很爽吧,注意有两个隐形陷阱:
1. 要求的是最短路径长度,不是说变到目标就行了,所以上个题只要碰到就该返回levels了。(这里还有个终极陷阱下面会说)。
2. levels中是记录了可以到达的点没错,但是!并不是互相都通的。这层两个,下层2个,哪个可以到哪个是还要再次check的。针对这点,网上有很多自创class的解法,相当于类似双边linkedlist的节奏,就省去了再次检查的麻烦。博主还是check了一遍。
于是思路变成这样:
有了levels之后,每次还要check下一层的一个元素是不是和目前temp的最后一个元素match,match才能进一步dfs,否则换一个check。因为match还是string差一位的match检查,然后每一个都要对每一层的元素check,所以相当耗时。。
不过为的就是写出自己理解的代码,这个题如此trivial,能按照自己的理解写出来,跑通就可以了。
上自己写的代码,耗时680MS,函数是static因为在IDE中测试debug了很久,一个个function打印变量找错。。
先说上面提到的终极陷阱,碰到target返回levels没错,但是!!!!!!!比如a,c, wordlist是a,b,c。level 0 是a没问题,然后变化a, b符合放到了level1,没问题,变化到c 发现match target,请问直接返回levels吗?NO。要删除b这一层的元素,来满足最短。
public class Solution { public static List<List<String>> findLadders(String start, String end, Set<String> wordList) { List<List<String>> res = new ArrayList<List<String>>(); List<List<String>> levels = ladderLength(start, end, wordList); if (levels.size()==0) return res; List<String> temp = new ArrayList<String>(); temp.add(start); placeInto(end, 1, temp, levels, res); return res; } static boolean canMatch(String a, String b){ if (a.length()!=b.length()) return false; int count=0; for (int i=0; i<a.length(); i++){ if(a.charAt(i)==b.charAt(i)) count++; } return count==a.length()-1; } static List<List<String>> ladderLength(String beginWord, String endWord, Set<String> wordDict) { List<List<String>> cache = new ArrayList<List<String>>(); LinkedList<String> queue = new LinkedList<String>(); LinkedList<Integer> level = new LinkedList<Integer>(); if (beginWord == null || endWord == null) return cache; queue.add(beginWord); level.add(0); wordDict.remove(beginWord); List<String> firstLevel = new ArrayList<String>(); firstLevel.add(beginWord); cache.add(firstLevel); while(queue.size()>0){ String temp = queue.removeFirst(); int lvl = level.removeFirst(); char[] cur = temp.toCharArray(); for(int i=0; i<cur.length; i++){ char tempchar = cur[i]; for (int j=0; j<26; j++){ if (tempchar=='a'+j) continue; cur[i]=(char)('a'+j); String curString = new String(cur); if(curString.equals(endWord)){ if(cache.size()==lvl+2) cache.remove(cache.size()-1); //////////////////////// return cache; } if(wordDict.contains(curString)){ wordDict.remove(curString); queue.add(curString); level.add(lvl+1); if (lvl+1==cache.size()) cache.add(new ArrayList<String>()); cache.get(lvl+1).add(curString); } } cur[i] = tempchar; } } return new ArrayList<List<String>>(); } static void placeInto(String end, int curlevel, List<String> temp, List<List<String>> levels, List<List<String>> res){ if (curlevel==levels.size()){ if(canMatch(temp.get(temp.size()-1), end)){ temp.add(end); res.add(new ArrayList<String>(temp)); temp.remove(temp.size()-1); return; } }else{ List<String> words = levels.get(curlevel); for(int i=0; i<words.size(); i++){ String word = words.get(i); if (canMatch(temp.get(temp.size()-1), word)){ temp.add(word); placeInto(end, curlevel+1, temp, levels, res); temp.remove(temp.size()-1); } } } }}
其实想想,通过这个题更想锻炼的是解题的耐心和debug的能力吧,具体的知识点bfs, dfs, backtracking相信大家都会,这题就是强行揉到一起。想清楚怎么做就够了。
- Leetcode 127. Word Ladder I & 126. Word Ladder II
- 【LeetCode】word ladder I&& II
- 126. Word Ladder II 、 127. Word Ladder(leetcode BFS+DFS)
- 126. Word Ladder I & II
- Word Ladder I && II
- [Leetcode] 126. Word Ladder II
- [leetcode] 126.Word Ladder II
- leetcode 126. Word Ladder II
- Leetcode 126. Word Ladder II
- Leetcode 126. Word Ladder II
- [leetcode] 126. Word Ladder II
- [LeetCode] 126. Word Ladder II
- 【LeetCode】126. Word Ladder II
- LeetCode 126. Word Ladder II
- LeetCode 126. Word Ladder II
- Leetcode 126. Word Ladder II
- leetcode 126. Word Ladder II
- leetcode 126. Word Ladder II
- iOS SDWebImage源码研究(三)
- BestCoder Round #79 1002/hdu 5661 Claris and XOR 贪心
- android调试工具DDMS的使用详解
- iOS 支持最新版本调试的文件路径备忘录
- Applications(ZOJ3705)
- Leetcode 127. Word Ladder I & 126. Word Ladder II
- 使用nginx搭建https服务器
- 为您揭开runtime的神秘面纱
- Java 线程同步 生产消费问题
- 重新认识数据驱动
- [leetcode] 229. Majority Element II
- IE盒子模型和标准盒子模型区别
- MD5初涉猎
- pip 命令相关错误