[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;
                }
            }
        }
    }
}

  

posted @ 2013-10-01 20:20  阿牧遥  阅读(598)  评论(0编辑  收藏  举报