问题描述
给定两个字符串,记为start和to,再给定一个字符串列表list,list中一定包含to list中没有重复字符串,所有的字符串都是小写的。
规定: start每次只能改变一个字符,最终的目标是彻底变成to,但是每次变成的新字符串必须在list 中存在。
请返回所有最短的变换路径。
【举例】
start="abc",end="cab",list={"cab","acc","cbc","ccc","cac","cbb","aab","abb"}
转换路径的方法有很多种,但所有最短的转换路径如下:
abc -> abb -> aab -> cab
abc -> abb -> cbb -> cab
abc -> cbc -> cac -> cab
abc -> cbc -> cbb -> cab
思路
思路一:
我们建立一个邻居表,邻居的定义是:一个字符串通过改变一个字符就可以变成另一个字符串,那么另一个字符串就是这个字符的邻居。我们遍历每一个字符串查看后面的字符是否是当前字符串的邻居,即可得到我们需要的邻居表。邻居表的建立需要的时间复杂度为:O(N^2),我们在遍历当前的字符串就可以得到本题答案,算法的整体时间复杂度为:O(N^2*K),其中K表示字符串的长度。
思路二:
同样建立邻居表,不过同思路以不同的。我们把给定的字符串列表list全部放在一个hashmap中,由于本题中通过hashmap查找指定字符串时不能忽略字符串的长度,所以查找字符串的时间复杂度由原来的O(1)转变为O(K)。根据题目我们知道所有的字符串都是小写的所以每个字符的变化只有24种,我们列出开始字符串邻居的所有可能性(时间复杂度为:O(25*k)),在hashmap中查看是否有这种情况的邻居即可。整体的时间复杂度为O(K^2)。
我们还需要建立距离表,其含义是该字符串到开始字符串的距离是多少。
- 我们可以提前预处理出 start 和 list 中的每个字符串变换后的可能的下一个字符串,并根据这个建图
- BFS 找出 start 到 to 的最短路径(注意:找到最短路径不要提前退出,需要把所有状态都处理掉,后面会用到)
- 根据第二步的结果和状态之间的距离关系,DFS 搜索路径(重要:根据第二步的状态可大大减少搜索状态)
- 将路径变换成字符串,并按照字典序从小到大排序并输出
代码
这里我们演示思路二的代码
public static List<List<String>> findMinPaths(String start, String end, List<String> list) {
list.add(start);
HashMap<String, ArrayList<String>> nexts = getNexts(list);
HashMap<String, Integer> distances = getDistances(start, nexts);
LinkedList<String> pathList = new LinkedList<>();
List<List<String>> res = new ArrayList<>();
getShortestPaths(start, end, nexts, distances, pathList, res);
return res;
}
public static HashMap<String, ArrayList<String>> getNexts(List<String> words) {
Set<String> dict = new HashSet<>(words);
HashMap<String, ArrayList<String>> nexts = new HashMap<>();
for (int i = 0; i < words.size(); i++) {
nexts.put(words.get(i), getNext(words.get(i), dict));
}
return nexts;
}
public static ArrayList<String> getNext(String word, Set<String> dict) {
ArrayList<String> res = new ArrayList<>();
char[] chs = word.toCharArray();
for (char cur = 'a'; cur <= 'z'; cur++) {
for (int i = 0; i < chs.length; i++) {
if (chs[i] != cur) {
char tmp = chs[i];
chs[i] = cur;
if (dict.contains(String.valueOf(chs))) {
res.add(String.valueOf(chs));
}
chs[i] = tmp;
}
}
}
return res;
}
public static HashMap<String, Integer> getDistances(String start,
HashMap<String,
ArrayList<String>> nexts) {
HashMap<String, Integer> distances = new HashMap<>();
distances.put(start, 0);
Queue<String> queue = new LinkedList<>();
queue.add(start);
HashSet<String> set = new HashSet<>();
set.add(start);
while (!queue.isEmpty()) {
String cur = queue.poll();
for (String next : nexts.get(cur)) {
if (!set.contains(next)) {
distances.put(next, distances.get(cur) + 1);
queue.add(next);
set.add(next);
}
}
}
return distances;
}
private static void getShortestPaths(String cur, String to,
HashMap<String, ArrayList<String>> nexts,
HashMap<String, Integer> distances,
LinkedList<String> path,
List<List<String>> res) {
path.add(cur);
if (to.equals(cur)) {
res.add(new LinkedList<String>(path));
} else {
for (String next : nexts.get(cur)) {
if (distances.get(next) == distances.get(cur) + 1) {
getShortestPaths(next, to, nexts, distances, path, res);
}
}
}
path.pollLast();
}
public static void main(String[] args) {
String start = "abc";
String end = "cab";
String[] test = {"cab", "acc", "cbc", "ccc", "cac", "cbb", "aab", "abb"};
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < test.length; i++) {
list.add(test[i]);
}
List<List<String>> res = findMinPaths(start, end, list);
System.out.println(res);
}