您的位置:首页 > 其它

[leetcode]Word Ladder II

2013-10-01 20:20 417 查看
因为搜索所有答案,所有我有了个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"在两个序列里都出现了),因为出现过的单词下次再出现肯定不是最短路了。经过先辈的试验,也光荣超时了。

参考:/article/5753406.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;
}
}
}
}
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: