開啟章節選單
10150 Doublets
題目連結
題目敘述
Doublets是一對單字,兩者恰好只有一個字母不同
例如 “booster” 與 “rooster”,或 “rooster” 與 “roaster”,或 “roaster” 與 “roasted”
給定一個最多包含 25143 個小寫單字的字典
每個單字長度不超過 16 個字母
接著,給定若干對單字
對於每一對單字
請找出一個最短的單字序列
該序列以第一個單字開始
以第二個單字結束
且序列中任意相鄰的兩個單字都是一對 雙字
例如,如果給定的輸入單字對是 “booster” 和 “roasted”
一種可能的解法為:
booster → rooster → roaster → roasted
(假設這些單字都存在於字典中)。
輸入格式
輸入首先是一個字典,接著是若干對輸入單字。
- 字典由若干單字構成,每行一個單字,以一個空行結束。
- 接下來的每一行包含一對單字,兩者以空格分隔。
輸出格式
對於每一對輸入單字,輸出若干行:
- 從第一個單字開始,每一行輸出序列中的一個單字,直到最後一行為第二個單字。
- 序列中任意相鄰的兩行所代表的單字必須構成一對 Doublets。
- 如果存在多種最短解,輸出任意一種。
- 若不存在可行解,則輸出一行:
No solution.
5. 不同測試用例之間輸出一個空行作為分隔。
## 解題思路
1. 字典構建:將所有輸入的單字讀取至一個列表,作為圖的節點。
2. 圖的構建:針對同長度的任意兩個單字,若僅有一個字母不同(Doublets 條件),則在兩者之間新增一條無向邊,形成鄰接表。
3. BFS 搜索:對於每一對查詢的起點與終點單字,採用廣度優先搜尋(BFS)以找到最短轉換序列。
- 使用一個佇列儲存待訪問節點,並以兩個 map 記錄已訪問狀態與前驅節點。
- 當當前節點等於終點時,即可停止搜索,因為 BFS 保證首次到達即為最短路徑。
4. 路徑重建:若存在可行解,根據前驅映射從終點回溯至起點,並反向輸出;若無解,則印出「No solution.」。
5. 多組查詢:輸出結果之間以空行分隔。
## 程式碼
```cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
vector<string> word;
while (getline(cin, s), s != "")
word.push_back(s); // 讀取整個字典:逐行讀入單字,直到遇到空行終止
map<string, vector<string>>
mp; // 構建鄰接表:mp[a] 存放與單字 a 僅有一字母差異的所有單字
for (auto &a : word)
for (auto &b : word)
if (a.size() == b.size()) {
int diff = 0;
for (int i = 0; i < a.size() && diff < 2; i++)
diff += a[i] !=
b[i]; // 計算兩單字之間的字母差異數量,差異超過 1 時提前終止
if (diff == 1) mp[a].push_back(b); // 若僅差異一位,則在圖中連接 a 與 b
}
string sta, end;
int t = 0;
while (cin >> sta >> end) { // 處理多組查詢:每行包含起點 sta 與終點 end
if (t++) cout << '\n'; // 在每組輸出之間印出空行
map<string, int> vis; // 記錄是否已訪問
map<string, string> path; // 保存每個節點的連接關係,用於回溯路徑
queue<string> q;
// 初始化 BFS:將起點加入佇列並標記已訪問
q.push(sta), vis[sta] = true;
while (!q.empty()) { // 執行 BFS,直到佇列為空或找到終點
string now = q.front();
q.pop();
if (now == end) break; // 找到終點即可停止搜尋
for (auto i : mp[now])
if (!vis[i]) // 若相連且尚未拜訪過
vis[i] = true, // 標記為已訪問
q.push(i), // 將相連結點加入佇列
path[i] = now; // 記錄從 i 返回 now 的父節點
}
string ans;
if (path[end] == "")
cout << "No solution.\n"; // 若 path 中沒有 end,代表無可達路徑
else { // 反向回溯:從 end -> ... -> sta
while (end != sta)
ans += string(end.rbegin(), end.rend()) + "\n", end = path[end];
cout << sta + string(ans.rbegin(), ans.rend()) << '\n';
}
}
}