zoukankan      html  css  js  c++  java
  • [leetcode]Word Ladder II

    因为搜索所有答案,所有我有了个DFS的方案。但后来一看,超时又错误,因为看错题,求的是所有最短路径的解。

    public class Solution {
    	private ArrayList<ArrayList<String>> ans = new ArrayList<ArrayList<String>>();
    	private HashSet<String> visited = new HashSet<String>();
    	HashSet<String> dict = null;
    	private String start = null;
    	private String end = null;
        public ArrayList<ArrayList<String>> findLadders(String start, String end, HashSet<String> dict) {
            ans.clear();
    		visited.clear();
    		this.start = start;
    		this.end = end;
    		this.dict = dict;
    		dict.add(start);
    		dict.add(end);
    		ArrayList<String> arr = new ArrayList<String>();
    		arr.add(start);
    		sub(start, arr);
    		return ans;
        }
    	
    	private void sub(String s, ArrayList<String> arr)
    	{
    		if (s.equals(end))
    		{
    			ans.add(new ArrayList(arr));
    		}
    		else
    		{
    			if (!dict.contains(s) || visited.contains(s))
    			{
    				return;
    			}
    			visited.add(s);
    			char[] ca = s.toCharArray();
    			for (int i = 0; i < s.length(); i++)
    			{
    				for (char c = 'a'; c <= 'z'; c++)
    				{
    					if (c != ca[i])
    					{
    						char tmp = ca[i];
    						ca[i] = c;
    						String nextS = new String(ca);
    						arr.add(nextS);
    						sub(nextS, arr);
    						arr.remove(arr.size()-1);
    						ca[i] = tmp;
    					}
    				}
    			}
    			visited.remove(s);
    		}
    	}
    }
    

    然后修改优化,第一个想到的办法是DFS过程中记录最短路径,之后比该路径长的就不继续了,最后把结果集大于该路径的都删除。这个方法本来就有bad case,果然超时。

    然后开始搜网上资料,和我一样,别人也想到用BFS。这样当搜到一个答案时,把这一层的全都做完就行了。而且,搜索时把之前层遍历到过的单词都放到visited集合里(本层的还不可以,因为比如"hot"在两个序列里都出现了),因为出现过的单词下次再出现肯定不是最短路了。经过先辈的试验,也光荣超时了。

    参考:http://www.cnblogs.com/shawnhue/archive/2013/06/05/leetcode_126.html http://www.cnblogs.com/obama/archive/2013/08/08/3247095.html http://blog.csdn.net/snakeling/article/details/9105147

    那么参考中说可以先建图,这样复杂性会大大下降,为什么呢?因为原来从start节点开始flood,每次展开都是26*L(L为单词长度)的可能性,这会指数级增长。现在是遍历字典里的词,每个单词尝试一下是否和能变换成其他单词,当词个数n很大时(远大于26*L),建图-邻接表-复杂度为O(n) [或O(26*L*n)]。(如果把字典中的单词两两比较是O(n^2))

    建完图之后可以用BFS(其实就是图求最短路的过程),因为已经建好了邻接表,所以复杂度为O(n),因为每个单词入queue一次,且状态转移为O(1)。可以用前驱表来记录路径,比如prev[1] = [2, 3, 0]表示有三条路(2 to 1) 或 (3 to 1) 或 (0 to 1)。

    如果还想优化,可以双向BFS,因为有起始节点和终结节点。

    下面是代码,写的很痛苦,注释如下:
    1.邻接表里存的是int类型的索引,i和j。
    2.建邻接表的过程中,从集合转了个string的array,这样可以用index索引。
    3.同时建立了一个index到string的map(中间变量),相当于反向查找表,方便查找。
    4.在求最段路径中,建立了一个前驱表。同时存了一个距离数组,表示到该点的最短路径。
    5.其实是一个求最短路径的算法,这里如果前驱表不为空,表示已经访问过了。
    6.从前驱表建立最终结果也是一个递归的过程。

    import java.util.*;
    public class Solution {
        private String start;
        private String end;
        private HashSet<String> dict;
        private ArrayList<ArrayList<String>> ans = new ArrayList<ArrayList<String>>();
        private ArrayList<String> words = new ArrayList<String>();
        public ArrayList<ArrayList<String>> findLadders(String start, String end, HashSet<String> dict) {
            ans.clear();
            words.clear();
            this.start = start;
            this.end = end;
            this.dict = dict;
            // wrong, start could be end
            dict.add(start);
            dict.add(end);
            for (String s : dict)
            {
                words.add(s);
            }
            // build adjacent list
            ArrayList<ArrayList<Integer>> adj = new ArrayList<ArrayList<Integer>>();
            HashMap<String, Integer> ids = new HashMap<String, Integer>(); // the map from word to its id in array words
            for (int i = 0; i < words.size(); i++)
            {
                ids.put(words.get(i), i);
                adj.add(new ArrayList<Integer>());
            }
            this.buildGraph(adj, ids);
            // find the start and end index
            int vs = 0;
            for (; !words.get(vs).equals(start); vs++);
            int ve = 0;
            for (; !words.get(ve).equals(end); ve++);
            // find the paths
            Queue<Integer> que = new LinkedList<Integer>();
            int[] dist = new int[dict.size()]; // distance, distance[vs] = 0;
            for (int i = 0; i < dist.length; i++)
            {
                dist[i] = -1;
            }
            dist[vs] = 0;
            ArrayList<ArrayList<Integer>> prev = new ArrayList<ArrayList<Integer>>();
            for (int i = 0; i < dict.size(); i++)
            {
                prev.add(new ArrayList<Integer>());
            }
            for (int i : adj.get(vs))
            {
                que.offer(i);
                dist[i] = 1;
                prev.get(i).add(vs);
            }
            while (que.size() != 0)
            {
                int idx = que.poll();
                if (idx == ve) break; // the prev[ve] is already processed in previous iteration
                int d = dist[idx] + 1;
                for (int i : adj.get(idx))
                {
                    if (prev.get(i).size() == 0 && i != vs) // not visited
                    {
                        que.offer(i);
                        prev.get(i).add(idx);
                        dist[i] = d;
                    }
                    else if (dist[i] == d) // already visited, dist[i] should be <= d
                    {
                        prev.get(i).add(idx);
                    }
                }
            }
            // generate the paths
            ArrayList<Integer> path = new ArrayList<Integer>();
            genPath(prev, path, vs, ve);
            return ans;
        }
        
        private void genPath(ArrayList<ArrayList<Integer>> prev, ArrayList<Integer> path, int vs, int ve)
        {
            path.add(ve);
            if (ve == vs)
            {
                ArrayList<String> tmp = new ArrayList<String>();
                for (int i = path.size()-1; i >=0; i--)
                {
                    tmp.add(words.get(path.get(i)));
                }
                ans.add(tmp);
            }
            else
            {
                for (int i : prev.get(ve))
                {
                    genPath(prev, path, vs, i);
                }
            }
            path.remove(path.size()-1);
        }
        
        private void buildGraph(ArrayList<ArrayList<Integer>> adj, HashMap<String, Integer> ids)
        {
            for (int i = 0; i < words.size(); i++)
            {
                char[] ca = words.get(i).toCharArray();
                for (int j = 0; j < words.get(0).length(); j++)
                {
                    for (char c = 'a'; c <= 'z'; c++)
                    {
                        if (ca[j] == c) continue;
                        char tmp = ca[j];
                        ca[j] = c;
                        String s = new String(ca);
                        if (dict.contains(s))
                        {
                            adj.get(i).add(ids.get(s));
                        }
                        ca[j] = tmp;
                    }
                }
            }
        }
    }
    

      

  • 相关阅读:
    java之Arrays.asList
    MySql索引
    Maven私服搭建
    基于Docker的GitLab搭建
    ubuntu新建组合用户命令不管用
    Linux 安装jdk
    消息队列
    Netty之大动脉Pipeline
    Netty之大名鼎鼎的EventLoop
    Netty之揭开BootStrap 的神秘面纱
  • 原文地址:https://www.cnblogs.com/lautsie/p/3348751.html
Copyright © 2011-2022 走看看