BFS解决最短路问题_最小基因变化、单词接龙_C++
1. 题目解析
leetcode链接:https://leetcode.cn/problems/minimum-genetic-mutation/submissions/569463000/
基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 'A'
、'C'
、'G'
和 'T'
之一。
假设我们需要调查从基因序列 start
变为 end
所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。
- 例如,
"AACCGGTT" --> "AACCGGTA"
就是一次基因变化。
另有一个基因库 bank
记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库 bank
中)
给你两个基因序列 start
和 end
,以及一个基因库 bank
,请你找出并返回能够使 start
变化为 end
所需的最少变化次数。如果无法完成此基因变化,返回 -1 。
注意:起始基因序列 start
默认是有效的,但是它并不一定会出现在基因库中。
示例 1:
输入:start = "AACCGGTT", end = "AACCGGTA", bank = ["AACCGGTA"]
输出:1
示例 2:
输入:start = "AACCGGTT", end = "AAACGGTA", bank = ["AACCGGTA","AACCGCTA","AAACGGTA"]
输出:2
大白话分析:
- 一个基因要变化到另一个基因,一次只能变化一个字母,而且变化后形成的基因,必须存在于基因库中。
2. 算法分析
1. 转化为边权为1的最短路问题:
如图,假设起始基因为AA
,可以变化的字符只有A、C、T
,先对AA
进行变化,就可以得出四个不同的基因。假设目标基因是CT
,那么CT
其实可以通过AT
或CA
变化得到。这些变化关系和基因构成了一副无向图,并且边权唯一。而且图一定是有限的。
如此一来,就将该问题转化为了边权为1的最短路问题。
2. 实现细节:
- 每次进行基因变化时,需要判断变化产生的基因是否存在于基因库中。你当然可以暴力遍历基因库,但是这样效率很慢,我们可以将基因库中的基因放在一个哈希表中,提升效率。
- 还需要一个
vis
哈希表,来标记这个基因有没有被搜索过,避免重复搜索。 - 如果目标基因不在基因库中,则一定是无法通过变化得到的,直接返回-1;如果目标基因就等于起始基因,直接不需要变化了,直接返回0。
3. 代码实现
class Solution {
public:
int minMutation(string startGene, string endGene, vector<string>& bank)
{
unordered_set<string> vis; // 用来标记已经搜索过的状态
unordered_set<string> hash(bank.begin(), bank.end()); // 存储基因库里面的字符串
string change = "ACGT";
if (startGene == endGene) return 0;
if (!hash.count(endGene)) return -1;
queue<string> q;
q.push(startGene);
vis.insert(startGene);
int ret = 0;
while(q.size())
{
ret++;
int sz = q.size();
while(sz--)
{
string t = q.front();
q.pop();
for (int i = 0; i < 8; i++)
{
string tmp = t; // 细节,不能直接改变t
for (int j = 0; j < 4; j++)
{
tmp[i] = change[j];
if (hash.count(tmp) && !vis.count(tmp))
{
if (tmp == endGene) return ret;
q.push(tmp);
vis.insert(tmp);
}
}
}
}
}
return -1;
}
};
4. 举一反三:单词接龙
leetcode链接:https://leetcode.cn/problems/om3reC/description/
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList)
{
unordered_set<string> vis;
unordered_set<string> hash(wordList.begin(), wordList.end());
if (beginWord == endWord) return 1;
if (!hash.count(endWord)) return 0;
queue<string> q;
q.push(beginWord);
vis.insert(beginWord);
int count = 1;
while (q.size())
{
int sz = q.size();
count++;
while (sz--)
{
string t = q.front();
q.pop();
for (int i = 0; i < t.size(); i++)
{
string tmp = t;
for (char ch = 'a'; ch <= 'z'; ch++)
{
tmp[i] = ch;
if (hash.count(tmp) && !vis.count(tmp))
{
if (tmp == endWord) return count;
q.push(tmp);
vis.insert(tmp);
}
}
}
}
}
return 0;
}
};