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

                }

            }

        }

    }

}

  

你可能感兴趣的:(LeetCode)