1. 解析
题目大意,给定基因序列start和end,判断是否能从start序列经过基因突变变成end序列?如果可以,返回所经过的最小次数。一次基因突变只改变其中的一个字母。
2. 分析
刚开始看到这题,觉得好熟悉,似乎之前做过,不过我没想起来,我采用了比较直接的方法。首先,可以确定的是,如果经过能基因突变成end,那么end一定会在bank数组当中,所以,第一步先判断end是否存在数组。我用了DFS,即从数组里面选择下一个突变的序列,如果所选择的序列和start序列之间只存在一个字符不相同,则可以突变,否则,选取下一个;一直往下搜索,如果找到end状态,即意味着可以经过n次基因突变,将start转变成end,更新res。
实际上,这道题也可以用BFS求解。
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
if (!count(bank.begin(), bank.end(), end)) return -1;
int res = INT_MAX;
DFS(start, end, bank, 0, res, 0);
return res == INT_MAX ? -1 : res;
}
void DFS(string start, string& end, vector<string>& bank, int pos, int& res, int sub_res){
if (start == end){
res = min(res, sub_res);
return;
}
for (int i = pos; i < bank.size(); ++i){
if ((start == bank[i]) || ! mutations(start, bank[i])) continue;
DFS(bank[i], end, bank, pos + 1, res, sub_res + 1);
}
}
bool mutations(const string& gene1, const string& gene2){ //基因突变
int i;
for (i = 0; i < gene1.length(); ++i){
if (gene1[i] != gene2[i]) break;
}
if (i == gene1.length() - 1) return true;
return gene1.substr(i + 1) == gene2.substr(i + 1);
}
};
BFS解法:
这种解法和Word Ladder的思路是一样的,其实只是换了个说法。即将每次的26中可能状态替换成4种状态。
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank){
if (!count(bank.begin(), bank.end(), end)) return -1;
unordered_set<string> exist{bank.begin(), bank.end()};
unordered_set<string> visited;
queue<string> q{{start}};
int level = 0;
vector<char> genes{{'A', 'C', 'G', 'T'}};
while (!q.empty()){
for (int i = q.size(); i > 0; --i){
string cur = q.front();
q.pop();
if (cur == end) return level;
for (int j = 0; j < cur.length(); ++j){ //对当前序列的每个位置都进行基因突变,即用'A', 'C', 'G', 'T'分别去替换
char old = cur[j];
for (char ch : genes){
cur[j] = ch;
if (exist.count(cur) && !visited.count(cur)){
if (cur == end) return level + 1; //建议加上这一行,能大幅度提升效率,即已经检测到最终状态,就无须往下检测了
visited.insert(cur);
q.push(cur);
}
}
cur[j] = old;
}
}
level++; //检测下一层
}
return -1;
}
};