[LeetCode] Word Ladder II

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

Note:

  • All words have the same length.
  • All words contain only lowercase alphabetic characters.

这题做得真累,尝试了几种不同的解法,感觉一不小心就会超时了。

这题很明显和上一题一样,仍然应该使用BFS,关键是要保存所有的最短路径而非单个最短距离。如何保存呢?这里我使用了两种不同的思路。

思路1:自定义一个Node类,里面保存当前节点的所有前驱,然后再用DFS去从end到start递推获得所有路径。

思路2:使用Set<List<String>>。虽然结果需要的形式为List<List<String>>,可以先用Set保存各个路径来达到去重的目的,然后调用容器类的addAll方法即可。

和上一题比较不同的是,当前str为一个字典词,且也在map里时,需要进一步判断:如果这条路径的长度和当前所保存的路径长度相等,那么也应该把当前路径加入到路径集合中。但是这里不能把这个str再次加入到搜索队列,因为这个str已经出现在map中了,说明之前肯定已经搜索到了,在第一次搜索到的时候已经加入到了搜索队列中。如果再次加入搜索队列,则会导致重复搜索而超时。

思路1实现:

	public class Node {
		public int dist;
		public String str;
		public LinkedList<Node> prev;

		public Node(int dist, String str) {
			this.dist = dist;
			this.str = str;
			this.prev = new LinkedList<Node>();
		}

		public void addPrev(Node pNode) {
			prev.add(pNode);
		}
	}

	public List<List<String>> findLadders(String start, String end,
			Set<String> dict) {
		dict.add(end);

		// Key: the dictionary string; Value: Node.
		Map<String, Node> map = new HashMap<String, Node>();
		Queue<String> queue = new LinkedList<String>();

		Node startNode = new Node(1, start);
		queue.offer(start);
		map.put(start, startNode);

		List<List<String>> ret = new ArrayList<List<String>>();

		while (!queue.isEmpty()) {
			String str = queue.poll();

			if (str.equals(end)) {
				getPaths(map.get(end), map, new ArrayList<String>(), ret);
				return ret;
			}

			for (int i = 0; i < str.length(); i++) {
				for (int j = 0; j < 26; j++) {
					char c = (char) ('a' + j);
					String newStr = replace(str, i, c);

					// If a new word is explored.
					if (dict.contains(newStr)) {
						if (!map.containsKey(newStr)) {
							// Construct a new node.
							Node node = map.get(str);
							Node newNode = new Node(node.dist + 1, newStr);
							newNode.prev = new LinkedList<Node>();
							newNode.prev.add(node);

							map.put(newStr, newNode);
							queue.offer(newStr);
						} else {
							Node node = map.get(newStr);
							Node prevNode = map.get(str);

							// Increase the path set.
							if (node.dist == prevNode.dist + 1) {
								node.addPrev(prevNode);
								// queue.offer(newStr); // This will cause TLE.
							}
						}
					}
				}
			}
		}

		return ret; // Return an empty set.
	}


思路2实现:

	public List<List<String>> findLadders(String start, String end,
			Set<String> dict) {
		dict.add(end);

		// Key: the dictionary string; Value: Set<List<String>>.
		Map<String, Set<List<String>>> map = new HashMap<String, Set<List<String>>>();
		Queue<String> queue = new LinkedList<String>();

		List<String> startPath = new ArrayList<String>();
		startPath.add(start);
		Set<List<String>> startSet = new HashSet<List<String>>();
		startSet.add(startPath);
		queue.offer(start);
		map.put(start, startSet);

		List<List<String>> ret = new ArrayList<List<String>>();

		while (!queue.isEmpty()) {
			String str = queue.poll();

			if (str.equals(end)) {
				ret.addAll(map.get(end));
				return ret;
			}

			for (int i = 0; i < str.length(); i++) {
				for (int j = 0; j < 26; j++) {
					// Transform it into another word.
					String newStr = replace(str, i, (char) ('a' + j));

					// If a new word is explored.
					if (dict.contains(newStr)) {
						if (!map.containsKey(newStr)) {
							// Construct a new path set.
							Set<List<String>> prevSet = map.get(str);
							Set<List<String>> newSet = new HashSet<List<String>>();
							for (List<String> path : prevSet) {
								List<String> newPath = new ArrayList<String>(
										path);
								newPath.add(newStr);
								newSet.add(newPath);
							}

							map.put(newStr, newSet);
							queue.offer(newStr);
						} else {
							Set<List<String>> prevSet = map.get(str);
							Set<List<String>> newSet = map.get(newStr);

							Iterator<List<String>> prevIt = prevSet.iterator();
							Iterator<List<String>> newIt = newSet.iterator();

							// Increase the path set.
							if (prevIt.next().size() + 1 == newIt.next().size()) {
								for (List<String> path : prevSet) {
									List<String> newPath = new ArrayList<String>(
											path);
									newPath.add(newStr);
									newSet.add(newPath);
									// queue.offer(newStr); // This will cause TLE.
								}
							}
						}
					}
				}
			}
		}

		return ret; // Return an empty set.
	}

	// Replace a character at the given index of str, with c.
	private String replace(String str, int index, char c) {
		StringBuilder sb = new StringBuilder(str);
		sb.setCharAt(index, c);
		return sb.toString();
	}


思路2由于不需要使用DFS去还原重构所求路径,也不需要自定义类,所以代码会稍微简短点。但是频繁的对中间结果路径的拷贝导致效率相对偏低,思路一会更快一点。


另外有一段代码是两个思路都需要的实现的一个子函数,用于还原路径。

	// Get all the paths by using DFS.
	private void getPaths(Node end, Map<String, Node> map,
			List<String> curPath, List<List<String>> paths) {
		if (end == null) {
			paths.add(curPath);
			return;
		}

		curPath.add(0, end.str);
		if (!end.prev.isEmpty()) {
			for (Node prevNode : end.prev) {
				getPaths(prevNode, map, new ArrayList<String>(curPath), paths);
			}
		} else {
			getPaths(null, map, curPath, paths);
		}
	}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值