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从start找到end,在找的过程中需要记录前驱单词,再用DFS反向找回完整路径。

但是用Java实现上述过程会遇到TLE。为了能让用时尽可能短,有如下几点需要注意的地方:

1. 由于最后生成路径的时候,需要从end找到start构造ArrayList,即使用LinkList来协助构造,性能也不好。解决办法:不从start找end了,反过来从end找start,找到后,再从start往end构造路径,性能会有明显提升。

2. 在BFS过程中,需要替换String的每一位字符,先转换成char数组再操作,性能也会有明显提升。

3. 在BFS过程中,注意避免一些不必要的搜索,具体细节参考如下代码。

最终在LeetCode测试中,Java实现达到了800ms用时。

此外,双向BFS也可以用来提升性能,而且效果会十分显著,这里没有具体实现。

代码

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

public class WordLadderII {
	class WordWithLevel {
		String word;
		int level;

		public WordWithLevel(String word, int level) {
			this.word = word;
			this.level = level;
		}
	}

	private String end;
	private ArrayList<ArrayList<String>> result;
	private Map<String, List<String>> nextMap;

	public ArrayList<ArrayList<String>> findLadders(String start, String end,
			HashSet<String> dict) {
		result = new ArrayList<ArrayList<String>>();
		this.end = end;

		// unvisited words set
		Set<String> unVisited = new HashSet<String>();
		unVisited.addAll(dict);
		unVisited.add(start);
		unVisited.remove(end);

		// used to record the map info of <word : the words of next level>
		nextMap = new HashMap<String, List<String>>();
		for (String e : unVisited) {
			nextMap.put(e, new ArrayList<String>());
		}

		// BFS to search from the end to start
		Queue<WordWithLevel> queue = new LinkedList<WordWithLevel>();
		queue.add(new WordWithLevel(end, 0));
		boolean found = false;
		int finalLevel = Integer.MAX_VALUE;
		int currentLevel = 0;
		int preLevel = 0;
		Set<String> visitedWordsInThisLevel = new HashSet<String>();
		while (!queue.isEmpty()) {
			WordWithLevel wordWithLevel = queue.poll();
			String word = wordWithLevel.word;
			currentLevel = wordWithLevel.level;
			if (found && currentLevel > finalLevel) {
				break;
			}
			if (currentLevel > preLevel) {
				unVisited.removeAll(visitedWordsInThisLevel);
			}
			preLevel = currentLevel;
			char[] wordCharArray = word.toCharArray();
			for (int i = 0; i < word.length(); ++i) {
				char originalChar = wordCharArray[i];
				boolean foundInThisCycle = false;
				for (char c = 'a'; c <= 'z'; ++c) {
					wordCharArray[i] = c;
					String newWord = new String(wordCharArray);
					if (c != originalChar && unVisited.contains(newWord)) {
						nextMap.get(newWord).add(word);
						if (newWord.equals(start)) {
							found = true;
							finalLevel = currentLevel;
							foundInThisCycle = true;
							break;
						}
						if (visitedWordsInThisLevel.add(newWord)) {
							queue.add(new WordWithLevel(newWord,
									currentLevel + 1));
						}
					}
				}
				if (foundInThisCycle) {
					break;
				}
				wordCharArray[i] = originalChar;
			}
		}

		if (found) {
			// dfs to get the paths
			ArrayList<String> list = new ArrayList<String>();
			list.add(start);
			getPaths(start, list, finalLevel + 1);
		}
		return result;
	}

	private void getPaths(String currentWord, ArrayList<String> list, int level) {
		if (currentWord.equals(end)) {
			result.add(new ArrayList<String>(list));
		} else if (level > 0) {
			List<String> parentsSet = nextMap.get(currentWord);
			for (String parent : parentsSet) {
				list.add(parent);
				getPaths(parent, list, level - 1);
				list.remove(list.size() - 1);
			}
		}
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值