433. 最小基因变化
https://leetcode-cn.com/problems/minimum-genetic-mutation/.
解题思路
基于广度优先的搜索
- 为了提高性能,我们会采用双向搜索的方式
- 如何双向搜索呢?
和一般广度一样,唯一区别就是每次需要去判断用头还是尾去遍历
最基本指导原则就是:
- 优先使用数量小的遍历
- 为了方便快速查找,我们用set来替换queue
未考虑广搜的代码
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
int count = 0;
for(int i = 7,j=0; i >= 0 && j < 8; i--,j++){
if(start[i]!=end[i]){
start[i]=end[i];
bool isvalid = false;
if(find(bank.begin(),bank.end(),start)!=bank.end()){
count++;
isvalid = true;
}
else return -1;
}
}
return count;
}
};
BFS代码
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
// 先构建bank的字典,方便快速查找, 如果使用后会删除,避免循环遍历
unordered_set<string> bankSet;
for (string& b : bank)
{
bankSet.insert(b);
}
// 提前判断
if (bankSet.find(end) == bankSet.end())
{
return -1;
}
// 可替换的四个字符
char replaces[4] = {'A', 'C', 'G', 'T'};
// 构建初始化的两个头尾集合
unordered_set<string> heads = {start};
unordered_set<string> tails = {end};
// 一个临时变量用于保存当前集合
unordered_set<string> temp;
// 次数记录:按照每一层去累加
int rounds = 0;
while (!heads.empty() && !tails.empty())
{
++rounds;
// 取最小的set作为遍历的对象,最小一直在heads里
if (heads.size() > tails.size())
{
swap(heads, tails);
}
for (const string& head : heads)
{
string curr = head;
// 遍历每个字符
int n = curr.size();
for (int i = 0; i < n; ++i)
{
char old = curr[i];
// 替换四种可能性
for (int j = 0; j < 4; ++j)
{
curr[i] = replaces[j];
// cout << rounds << " : " << curr << " " << head << endl;
// 在尾巴里找到了,则直接返回
if (tails.find(curr) != tails.end())
{
return rounds;
}
else if (bankSet.find(curr) != bankSet.end())
{
bankSet.erase(curr);
temp.insert(curr);
}
}
// 必不可少的还原字符
curr[i] = old;
}
}
// 快速交换赋值给heads
swap(heads, temp);
temp.clear();
}
// 找不到则返回-1
return -1;
}
};