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;
}
};