給定起始單詞 beginWord、目標單詞 endWord、字典 wordList。每步只能改變一個字母,且中間單詞必須在字典中。求從 beginWord 到 endWord 的最短轉換序列長度(含首尾)。無法轉換返回 0。
例:hit → hot → dot → dog → cog,長度 5。
// 構建 word -> id 映射
// 雙重循環 O(V^2 * L) 檢查每對單詞是否相鄰
// BFS 在鄰接矩陣上跑
缺陷:$V \le 5000$,$V^2 = 2.5 \times 10^7$,配合字符串比較 $O(L)$ 導致預處理極慢,記憶體 $O(V^2)$ 過大。
核心洞見:不必預先構建圖。訪問到單詞 cur 時,枚舉其每個位置換成 'a'..'z',查詢新單詞是否在字典中($O(1)$ 哈希查找)。
unordered_set 存字典:查找 $O(1)$ 期望,避免 $O(V)$ 線性掃描。
class Solution {
public:
int ladderLength(string beginWord, string endWord, unordered_set& wordList) {
if (wordList.find(endWord) == wordList.end()) return 0;
queue q;
unordered_set visited;
q.push(beginWord);
visited.insert(beginWord);
int level = 1;
while (!q.empty()) {
int sz = q.size();
while (sz--) {
string cur = q.front(); q.pop();
if (cur == endWord) return level;
string next = cur;
for (int i = 0; i < cur.size(); ++i) {
char orig = next[i];
for (char c = 'a'; c <= 'z'; ++c) {
if (c == orig) continue;
next[i] = c;
if (wordList.count(next) && !visited.count(next)) {
visited.insert(next);
q.push(next);
}
}
next[i] = orig; // 恢復
}
}
++level;
}
return 0;
}
};
從 beginWord 和 endWord 同時擴展,每次擴展較小的前沿,相遇時返回。時間複雜度約降為 $O(b^{d/2})$($b$ 分支因子,$d$ 深度),對大字典顯著加速。
// 雙向 BFS 骨架
unordered_set beginSet{beginWord}, endSet{endWord}, visited;
int level = 1;
while (!beginSet.empty() && !endSet.empty()) {
if (beginSet.size() > endSet.size()) swap(beginSet, endSet);
unordered_set nextSet;
for (auto& w : beginSet) {
string nxt = w;
for (int i=0;i
單詞接龍是「隱式圖 BFS」的經典案例。關鍵技巧:
此思路廣泛適用於「狀態空間隱式、轉換規則簡單」的最短路徑問題(如數字變換、棋盤移動等)。