一条基因序列由一个带有8个字符的字符串表示,其中每个字符都属于 "A", "C", "G", "T"中的任意一个。
假设我们要调查一个基因序列的变化。一次基因变化意味着这个基因序列中的一个字符发生了变化。
例如,基因序列由"AACCGGTT" 变化至 "AACCGGTA" 即发生了一次基因变化。
与此同时,每一次基因变化的结果,都需要是一个合法的基因串,即该结果属于一个基因库。
现在给定3个参数 — start, end, bank,分别代表起始基因序列,目标基因序列及基因库,请找出能够使起始基因序列变化为目标基因序列所需的最少变化次数。如果无法实现目标变化,请返回 -1。
注意:
起始基因序列默认是合法的,但是它并不一定会出现在基因库中。
如果一个起始基因序列需要多次变化,那么它每一次变化之后的基因序列都必须是合法的。
假定起始基因序列与目标基因序列是不一样的。
示例 1:
start: "AACCGGTT"
end: "AACCGGTA"
bank: ["AACCGGTA"]
返回值: 1
解法一:宽度优先搜索+记忆化
public int minMutation(String start, String end, String[] bank) { // 定义三个集合,分别是合法基因集合,起始基因集合,目标基因集合,起始基因记忆集,目标基因记忆集 Set<String> dict = new HashSet<>(), st = new HashSet<>(), ed = new HashSet<>(), menSt = new HashSet<>(), menEd = new HashSet<>(); for(String s : bank) dict.add(s); // 基因库中不包含目标,则无法转换 if(!dict.contains(end)) return -1; st.add(start); ed.add(end); // 宽搜 return bfs(st, ed, menSt, menEd, dict, 0); } // 宽搜 private int bfs(Set<String> st, Set<String> ed, Set<String> menSt, Set<String> menEd, Set<String> dict, int len) { // 起始集合为空,那么就无法到达目标 if(st.size() == 0) return -1; // 优先从数量少的一端开始搜索,减少搜索量 if(st.size() > ed.size()) return bfs(ed, st, menEd, menSt, dict, len); Set<String> next = new HashSet<>(); char[] mode = {‘A‘, ‘C‘, ‘G‘, ‘T‘}; // 枚举起始集合可以一步转换的所有基因序列 for(String s : st) { StringBuilder temp = new StringBuilder(s); for(int i = 0; i < 8; i++) { for(int j = 0; j < 4; j++) { temp.setCharAt(i, mode[j]); String cur = temp.toString(); // 终点集合中包含了当前字符,那么直接返回步数 if(ed.contains(cur)) return len + 1; // 如果搜过了该种情况,就不能重复遍历 if(menSt.contains(cur)) continue; // 如果是合法序列,则加入下一个搜索集合中 if(dict.contains(cur)) { next.add(cur); menSt.add(cur); } temp.setCharAt(i, s.charAt(i)); } } } // 搜索下一层 return bfs(next, ed, menSt, menEd, dict, len + 1); }
解法二:dfs深度优先搜索
class Solution { int minStepCount = Integer.MAX_VALUE; public int minMutation(String start, String end, String[] bank) { dfs(new HashSet<String>(), 0, start, end, bank); return (minStepCount == Integer.MAX_VALUE) ? -1 : minStepCount; } private void dfs (HashSet<String> step, int stepCount, String current, String end, String[] bank) { if (current.equals(end)) minStepCount = Math.min(stepCount, minStepCount); for (String str: bank) { int diff = 0; for (int i = 0; i < str.length(); i++) if (current.charAt(i) != str.charAt(i)) if (++diff > 1) break; if (diff == 1 && !step.contains(str)) { step.add(str); dfs(step, stepCount + 1, str, end, bank); step.remove(str); } } } }