Description
A gene string can be represented by an 8-character long string, with choices from ‘A’, ‘C’, ‘G’, and ‘T’.
Suppose we need to investigate a mutation from a gene string start to a gene string end where one mutation is defined as one single character changed in the gene string.
For example, “AACCGGTT” --> “AACCGGTA” is one mutation.
There is also a gene bank bank that records all the valid gene mutations. A gene must be in bank to make it a valid gene string.
Given the two gene strings start and end and the gene bank bank, return the minimum number of mutations needed to mutate from start to end. If there is no such a mutation, return -1.
Note that the starting point is assumed to be valid, so it might not be included in the bank.
Examples
Example 1:
Input: start = “AACCGGTT”, end = “AACCGGTA”, bank = [“AACCGGTA”]
Output: 1
Example 2:
Input: start = “AACCGGTT”, end = “AAACGGTA”, bank = [“AACCGGTA”,“AACCGCTA”,“AAACGGTA”]
Output: 2
Example 3:
Input: start = “AAAAACCC”, end = “AACCCCCC”, bank = [“AAAACCCC”,“AAACCCCC”,“AACCCCCC”]
Output: 3
Constraints:
start.length == 8
end.length == 8
0 <= bank.length <= 10
bank[i].length == 8
start, end, and bank[i] consist of only the characters [‘A’, ‘C’, ‘G’, ‘T’].
思路
最开始的思路很简单,因为这其实也是求解最短路径的问题(用bfs)做,每个字符串都可以看作一个状态,一个状态到另一个状态之间的改变就是一条路径,根据这一点,可以构建一个函数”differOne“来判断两个状态是否是直接联通的两个状态(只相差一个单词),从而构建出邻接矩阵
根据邻接矩阵,做bfs,记录move-step,就可以得到最后的答案了,这个方法在这道题里面已经100%了
做完之后再去看发现大家都说这道题和127是一样的,而且他们提供了另一种说法,不是针对bank里面的字符串构建邻接矩阵,而是通过字符串改变gene,再判断改变之后的字符串是否在bank中,也就是对每一个状态,针对字符串中的每一个单词,遍历”A“,”C“,”G“,”T“四个方向,判断one-move可以到达的状态。这种做法在127里面是有效的,但在433中收效甚微(甚至比上面的纯bfs要慢)
这是因为这里的bank.length在0-10之间,遍历邻接矩阵会快许多,在127的5000个bank中,5000 >> word.length * 26,所以第二种方法更为适用
代码
邻接矩阵
class Solution {
public boolean differOne(String a, String b){
int count = 0;
for (int i = 0; i < 8; i++){
if (a.charAt(i) != b.charAt(i))
count ++;
}
return count == 1;
}
public int minMutation(String start, String end, String[] bank) {
int[][] matrix = new int[bank.length][bank.length];
// build matrix
for (int i = 0; i < bank.length; i++){
for (int j = i + 1; j < bank.length; j++) {
if (differOne(bank[i], bank[j])){
matrix[i][j] = 1;
matrix[j][i] = 1;
}
}
}
int level = 1;
List<Integer> queue = new ArrayList<>();
int[] hasVisit = new int[bank.length];
for (int i = 0; i < bank.length; i++){
if (differOne(start, bank[i])){
if (bank[i].equals(end))
return level;
queue.add(i);
hasVisit[i] = 1;
}
}
while (queue.size() > 0){
List<Integer> newQueue = new ArrayList<>();
level ++;
for (int num: queue){
for (int i = 0; i < bank.length; i++){
if (matrix[num][i] == 1 && hasVisit[i] == 0){
if (bank[i].equals(end))
return level;
hasVisit[i] = 1;
newQueue.add(i);
}
}
}
queue = new ArrayList<>(newQueue);
}
return -1;
}
}
遍历四个方向
class Solution {
public int minMutation(String start, String end, String[] bank) {
Set<String> bankSet = new HashSet<>();
for (String b: bank)
bankSet.add(b);
List<String> toVisit = new ArrayList<>();
Set<String> visited = new HashSet<>();
int level = 1;
toVisit.add(start);
while(toVisit.size() > 0){
List<String> newToVisitList = new ArrayList<>();
for (String pre: toVisit){
for (int i = 0; i < 8; i++){
for (String ch: new String[]{"A", "C", "G", "T"}){
StringBuilder sb = new StringBuilder(pre);
sb.replace(i, i + 1, ch);
String after = sb.toString();
if (!visited.contains(after) && bankSet.contains(after)){
if (after.equals(end))
return level;
visited.add(after);
newToVisitList.add(after);
}
}
}
}
toVisit = new ArrayList<>(newToVisitList);
level ++;
}
return -1;
}
}