单源最短路问题,我们常常使用BFS来解决,但实际题目中,不会直接的把题目的解法直白的告诉你,常常是以一种不相关的表述,所以我们刷题锻炼的就是如何从不相关的题目描述中提取出来这个题目真正想要表达的。
这里博主列出LeetCode的三个题目,基本都是没有直观的描述图,并且解法进本相同,套用单源BFS,以下题解(包含注释),可作为大家学习板子和理解题目使用。
LeetCode433. 最小基因变化
从start 到 end,其中每次操作只允许一个位置发生一次编码变化,而且变化的编码是限定的(A T C G).我们使用BFS来枚举每一步之后产生的编码序列,并判断,当前步产生的编码是否已经到达end,或者当前编码是否合法。
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
unordered_set<string> cnt;//合法编码,使用hash方便判断枚举的序列是否合法
unordered_set<string> see;//路径节点访问标志,图中一个节点走过就不能走了。
char keys[4] = {'A', 'C', 'G', 'T'}; //修改编码。
for (auto & w : bank) {
cnt.emplace(w);//初始化
}
if (start == end) {
return 0;//剪枝
}
if (!cnt.count(end)) {
return -1;//剪枝
}
queue<string> qu;//BFS 初始化队列
qu.emplace(start);
see.emplace(start);
int step = 1;
while (!qu.empty()) {
int sz = qu.size();//很重要,每一步产生的所有符合要求的序列都会入队,要对每种入队的序列进行接下来的枚举。
for (int i = 0; i < sz; i++) {
string curr = qu.front();
qu.pop();//取出合法序列
for (int j = 0; j < 8; j++) {
for (int k = 0; k < 4; k++) {//进行枚举
if (keys[k] != curr[j]) {
string next = curr;
next[j] = keys[k];
// cout<<next<<endl;
if (!visited.count(next) && cnt.count(next)) {//没有访问过,并且符合bank条件
if (next == end) {
return step; //找到了终点返回
}
// cout<<"--"<<next<<endl;
qu.emplace(next);//维护队列和see
see.emplace(next);
}
}
}
}
}
step++;//每一步之后 step++
}
return -1; //全部枚举后没有找到,返回-1
}
};
LeetCode127. 单词接龙
思想和上一个题目一模一样,处理的方式也相同,直接放上代码
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> cnt;
unordered_set<string> see;
for(auto a : wordList)
{
cnt.emplace(a);
}
if(!cnt.count(endWord))
return 0;
queue<string> q;
q.emplace(beginWord);
see.emplace(beginWord);
int step = 1;
while(!q.empty())
{
int sz = q.size();
for(int i = 0; i < sz; i++)
{
string cur = q.front();
q.pop();
for(int j = 0; j < beginWord.length(); j++)
{
for(int k = 0; k < 26; k++)
{
char c = 'a' + k;
if(c != cur[j])
{
string next = cur;
next[j] = c;
if(!see.count(next) && cnt.count(next))
{
if(next == endWord)
return ++step;
cout<<next<<endl;
q.emplace(next);
see.emplace(next);
}
}
}
}
}
step++;
}
return 0;
}
};
LeetCode752. 打开转盘锁
解法还是使用BFS,但是处理过程跟上面两个题目不同,这题是给了不能出现的数组,只需要在代码上判断上稍作修改。其中有一个读题的注意点:每次拨动一个转轮的一个数字(表示这个转轮的数字可以上下拨动,且只能拨动一次如 3可以变为1和4)所以在枚举的过程中处理一下,数字的拨动即可。
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
unordered_set<string> cnt;
unordered_set<string> see;
for(auto a : deadends)
cnt.emplace(a);
if(target == "0000")
return 0;
if(cnt.count("0000"))
return -1;
queue<string> q;
q.emplace("0000");
see.emplace("0000");
int step = 1;
while(!q.empty())
{
int sz = q.size();
for(int i = 0; i < sz; i++)
{
string cur = q.front();
q.pop();
for(int j = 0; j < 4; j++)
{
for(int t = 0; t < 2; t++)
{
char k;
if(t == 0)
k = (cur[j] == '9' ? '0' : cur[j] + 1);
else
k = (cur[j] == '0' ? '9' : cur[j] - 1);
string next = cur;
next[j] = k;
if(!see.count(next) && !cnt.count(next))
{
if(next == target)
return step;
// cout<<next<<endl;
see.emplace(next);
q.emplace(next);
}
}
}
}
step++;
}
return -1;
}
};