题目直达https://leetcode.cn/problems/word-ladder/description/
题目介绍
转换序列:是一个字符串转换序列
first_word -> s1 -> s2 -> ... -> sk -> last_word
:要求:相邻两个字符串只相差一个字符,并且除了first_word,其他字符串必须出现在指定字符串集合words当中。求最短字符串转换序列
示例:
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] 输出:5 解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
就是一个隐式单源无权最短路问题,朴素的BFS就能解决。(有很多类似的题目,比如基因突变序列,信息输送验证一类的)
但是这题的新意在于数学建模(hash、虚拟节点)和算法优化(bidirDFS、A-Star)。
朴素BFS
#include <unordered_map>
#include <string>
#include <vector>
#include <array>
#include <limits>
#include <queue>
constexpr int N { 50 };
constexpr int M { 10 };
std::unordered_map<std::string, int> hash;
std::array<std::array<int, N * M>, N * M> graph {};
std::array<int, N * M> counter;
std::array<int, N * M> distance;
int index {};
void hashing(std::string& Str){
if(!hash.count(Str)) hash[Str] = index ++;
}
void insert(std::string& Str){
hashing(Str);
int from { hash[Str] };
for(char& ref : Str){
char buffer { ref };
ref = '*';
hashing(Str);
int to { hash[Str] };
graph[from][counter[to] ++] = to;
graph[to][counter[from] ++] = from;
ref = buffer;
}
}
int ladder_length(std::string& begin, std::string& end, std::vector<std::string>& words){
for(auto& Str : words) insert(Str);
insert(begin);
if(!hash.count(end)) return 0;
distance.fill(std::numeric_limits<int>::max());
distance[hash[begin]] = 0;
std::queue<int> Q;
Q.push(hash[begin]);
while(!Q.empty()){
int current { Q.front() };
Q.pop();
if(current == hash[end]) return distance[hash[end]] / 2 + 1;
for(int i {}; i < counter[current]; ++i){
int next { graph[current][i] };
if(distance[next] == std::numeric_limits<int>::max()){
distance[next] = distance[current] + 1;
Q.push(next);
}
}
}
return 0;
}
双向BFS
#include <unordered_map>
#include <string>
#include <vector>
#include <array>
#include <limits>
#include <queue>
constexpr int N { 50 };
constexpr int M { 10 };
std::unordered_map<std::string, int> hash;
std::array<std::array<int, N * M>, N * M> graph {};
std::array<int, N * M> counter;
std::array<int, N * M> distance;
std::array<int, N * M> rdistance;
int index {};
void hashing(std::string& Str){
if(!hash.count(Str)) hash[Str] = index ++;
}
void insert(std::string& Str){
hashing(Str);
int from { hash[Str] };
for(char& ref : Str){
char buffer { ref };
ref = '*';
hashing(Str);
int to { hash[Str] };
graph[from][counter[to] ++] = to;
graph[to][counter[from] ++] = from;
ref = buffer;
}
}
int ladder_length(std::string& first, std::string& last, std::vector<std::string>& words){
for(auto& s : words) insert(s);
insert(first);
if(!hash.count(last)) return 0;
distance.fill(std::numeric_limits<int>::max());
rdistance.fill(std::numeric_limits<int>::max());
distance[hash[first]] = rdistance[hash[last]] = 0;
std::queue<int> Q;
std::queue<int> rQ;
Q.push(hash[first]);
rQ.push(hash[last]);
while(!Q.empty() && !rQ.empty()){
int s_Q = Q.size();
int s_rQ = rQ.size();
while(s_Q--){
auto current {Q.front()};
Q.pop();
if(!(rdistance[current] == std::numeric_limits<int>::max()))
return (distance[current] + rdistance[current]) / 2 + 1;
for(int i {}; i < counter[current]; +i){
int next {graph[current][i]};
if(distance[next] == std::numeric_limits<int>::max()){
distance[next] = distance[current] + 1;
Q.push(next);
}
}
}
while(s_rQ--){
auto current {rQ.front()};
rQ.pop();
if(!(distance[current] == std::numeric_limits<int>::max()))
return (distance[current] + rdistance[current]) / 2 + 1;
for(int i {}; i < counter[current]; +i){
int next {graph[current][i]};
if(rdistance[next] == std::numeric_limits<int>::max()){
rdistance[next] = rdistance[current] + 1;
Q.push(next);
}
}
}
}
return 0;
}
A-Star BFS
#include <unordered_map>
#include <vector>
#include <array>
#include <limits>
#include <string>
#include <queue>
#include <ranges>
constexpr int N { 64 };
std::unordered_map<std::string const&, int> hash;
std::unordered_map<std::int, std::string const&> rehash;
std::array<std::array<int, N>, N> graph {};
std::array<int, N> counter {};
std::array<int, N> distance {};
std::array<int, N> value {};
int ladder_length(std::string& first, std::string& last, std::vector<std::string>& words){
int index {};
auto len { words[0].length() };
auto f { [&](std::string& source, std::string& target){
int __value__ {};
for(auto i {0U}; i < len; ++i) __value__ += target[i] == source[i];
return __value__;
} };
for(auto& s : words) {
hash[s] = index;
rehash[index ++] = s;
value[hash[s]] = f(s, last);
}
for(auto &s : words)
for(auto &t : words | std::views::filter([&s](auto t){ return t == s; }))
if(f(s, t) == 1){
graph[hash[s]][counter[hash[s]] ++] = hash[t];
graph[hash[t]][counter[hash[t]] ++] = hash[s];
}
if(!hash.count(last)) return 0;
distance.fill(std::numeric_limits<int>::max());
distance[hash[first]] = 0;
std::priority_queue<std::pair<int, int>> Q;
Q.emplace(0, hash[first]);
while(!Q.empty()){
auto [dummy, current] = Q.top();
Q.pop();
if(current == hash[last]) return distance[hash[last]];
for(int i {}; i < counter[current]; ++i){
int next { graph[current][i] };
if(distance[next] == std::numeric_limits<int>::max()){
distance[next] = distance[current] + 1;
Q.emplace(distance[next] + f(rehash[current], rehash[next]));
}
}
}
return 0;
}