题目大意
给定一个字典,一个起始单词start,一个终止单词end。使用字典内的单词作为中间状态,每次变换start的一个字母直到原起始单词变为终止单词end。注:单词长度一致。问:(1)最少多少步能完成操作?(2)输出所有的变换过程
解题思路
首先自然想一想暴力的方法,穷举就不用说了,这里我想到的暴力是先预处理字典中的单词,建立distance[i][j]数组保存那些字母变化为1的单词,即当distance[i][j] == true时单词i和单词j只有一个不同的字母,这样就能建立起一个二维矩阵(图),然后在这个图上进行深搜去查找最短的start->end的路径长度,同时也能存储下每次的变换路径。可这题要是这样就能水过那就未免太对不起它11%的过题率了,此方法显然效率太低,字典内的单词最高高达4000+,所以不出意外应该是TLE(亲测)。
直接深搜肯定是不行,注意到题目是要求最少的步数,那么很容易就能过渡到广搜的方法。单纯将上述算法改成广搜的话,那还远远不够,注意到单词长度都不是很长,而单词数目却非常多,那就要想办法避其锋芒,华丽丽滴优化就这么来了:广搜时,当要扩展某一节点时,直接枚举每一个位置的字母(从‘a’~‘z’),如果变化后的单词在字典中出现则将此单词入队继续广搜。这样一来就能解决问题1。这里同样的方法,我使用C++没过,但是用Java却过了,I wonder to know why.
接下来是问题2,:如何输出所有的变换过程?我首先想到了用一个结构,里面定义一个fa的节点,它在广搜时记录每一次用于扩展的父节点(扩展后的节点指向扩展它的节点),这样广搜退出时就能向前一直找到整个路径,全算法过程只要bfs一次就行了。原以为这样能够过去,可是还是拿了个TLE,没办法,只能硬着头皮想优化。第二种方法显然,既然是广搜,我可以从头和尾同时开始广搜,当两个广搜的节点碰撞时,说明已经形成了一条路径,那么在结构中使用backward(类似于原来的fa)、forward分别记录路径前半部份广搜节点的前驱和后半部分广搜节点的后继,这样当这两个方向的广搜相遇时,从相遇的节点分别沿前驱后继各走一次就能得到最终路径。这个思路用一个形象的比喻就好比在一个池塘中同时丢下两块石子,当它们的波纹碰撞时就意味着找到了最短的变换路径,此时只要沿着之前记录的前驱后继将路径存储下来就行了。满以为这种方法肯定能够AC,结果还是拿到了一个血淋淋的TLE。。。啊~~多么痛的领悟!!!。。。
看来裸广搜还是不行了,最后只能想到图论的方法,将字典抽象成一张图(由广搜层次决定节点的连接),广搜的时候建立起节点和它广搜层次的映射(使用hashmap),然后从end开始向回深搜这个广度优先图,获得最终路径(注意,从广度优先图的最终end开始搜索,能够有效增加速度,因为start节点只有一个,避免了对end的遍历)这种方法终于AC问题2
总结
思考一个问题时,可以先从暴力入手获得一些信息(基本解题方法、最差复杂度等)然后结合题目的一些特性使用一些数据结构逐步优化这种暴力方法从而得到最优或者是次优方法。
积累和勤奋都很重要,继续努力!!
代码(Java)
问题一:
public class Solution { public int bfs(String start, String end, Set<String> dict) { Queue <String> que = new LinkedList <String>(); while (!que.isEmpty())que.poll(); int sum = 1; que.add(start); que.add("\0"); Set<String> visited = new HashSet<String>(); visited.clear(); //visited.add(start); que.add(start); //for (it_i = dict.begin(); it_i != dict.end(); ++it_i)visited[(*it_i)] = true; int word_len = start.length(); //System.out.print(word_len); //visited[head.str] = true; //string str_tmp; while (!que.isEmpty()){ String tmp = que.poll(); if (tmp == "\0"){ sum ++; if (que.isEmpty()){ return 0; } else { if (tmp.equals(end))return sum+1; tmp=que.element(); que.add("\0"); } } //int jud = count_diff(tmp.str,end); //cout<<"cmp:"<<tmp.str<<' '<< end<<endl; //if (tmp.str==end) return tmp.sum+1; for (int i = 0; i < word_len; i ++){ char sav = tmp.charAt(i); //System.out.println("i:"+i+' '+sav); for (char j = 'a'; j <= 'z'; j ++){ tmp=tmp.substring(0, i)+j+tmp.substring(i+1); if (!visited.contains(tmp) && dict.contains(tmp)){// //System.out.println(tmp); if (tmp.equals(end))return sum+1; visited.add(tmp); que.add(tmp); } } tmp=tmp.substring(0, i)+sav+tmp.substring(i+1); } } return 0; } public int ladderLength(String start, String end, Set<String> dict) { //initalize the distance array to record the distance between each word in the alphabetic table if (start == end)return 1; dict.add(end); return bfs(start,end,dict); } }
public class Solution { HashMap<String,Integer> graph = new HashMap<String,Integer>(); List<List<String>> final_ans = new LinkedList<List<String>>(); List<String> path = new LinkedList<String>(); void bfs(int word_len, String start, String end, Set<String> dict){ Queue <String> que = new LinkedList <String>(); que.add(start); graph.put(start, 1); int deep = 1; while (!que.isEmpty()){ String head = que.poll(); if (head.equals(end)){ //graph.put(end, graph.get(head)+1); continue; } for (int i = 0; i < word_len; i ++){ for (char j = 'a'; j <= 'z'; j ++){ String tail = head.substring(0,i)+j+head.substring(i+1); if (dict.contains(tail)&&!graph.containsKey(tail)){ deep = graph.get(head); graph.put(tail, deep+1); que.add(tail); } } } } } void dfs(int word_len, String cur, String end, Set<String> dict, List<String> newPath){ if (cur.equals(end)){ newPath.add(end); Collections.reverse(newPath); // Iterator<String> it = newPath.iterator(); // System.out.println("path:"); // while (it.hasNext()){ // System.out.println(it.next()); // // } final_ans.add(newPath); return ; } newPath.add(cur); int next_deep = 0; if (graph.get(cur)!=null) next_deep = graph.get(cur); for (int i = 0; i < word_len; i ++){ for (char j = 'a'; j <= 'z'; j ++){ String next = cur.substring(0,i)+j+cur.substring(i+1); if (graph.get(next)!=null && graph.get(next) == next_deep-1){ List<String> newPathArray = new LinkedList<String>(newPath); dfs(word_len, next, end, dict, newPathArray); } } } } public List<List<String>> findLadders(String start, String end, Set<String> dict) { //dict.add(end); bfs(start.length(), start, end, dict); // Iterator it = graph.entrySet().iterator(); // while (it.hasNext()){ // Map.Entry entry=(Map.Entry) it.next(); // System.out.println(entry.getKey().toString()+" "+entry.getValue().toString()); // // } dfs(start.length(), end, start, dict, path); return final_ans; } }