給定 N 個單詞(1 <= N <= 10000,每詞長 ≤ 100,僅小寫字母)。判斷是否能重新排序這些單詞,使得每個單詞的首字母等於前一個單詞的尾字母。輸出 "Possible" 或 "Impossible"。
範例:
輸入: 2 ok ok → Impossible (o→k, o→k 無法連接) 輸入: 2 ok ko → Possible (o→k, k→o 可成環)
將 26 個小寫字母視為圖的節點,每個單詞是一條有向邊:從首字母指向尾字母。問題轉化為:該有向圖是否存在歐拉路徑(或歐拉迴路)?
有向圖存在歐拉路徑的充要條件:
原碼採用貪心嘗試連接,未嚴格檢查連通性與度數條件,可能對某些案例誤判。下面給出正確的歐拉路徑判定實作。
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAX_WORDS 10000
#define MAX_LEN 100
int main() {
int N;
while (scanf("%d", &N) == 1) {
int in[26] = {0}, out[26] = {0};
int parent[26];
for (int i = 0; i < 26; i++) parent[i] = i; // 並查集初始化
char word[MAX_LEN + 1];
for (int i = 0; i < N; i++) {
scanf("%s", word);
int u = word[0] - 'a';
int v = word[strlen(word) - 1] - 'a';
out[u]++; in[v]++;
// 並查集合併(無向連通性)
auto find = [&](int x) {
while (parent[x] != x) x = parent[x] = parent[parent[x]];
return x;
};
int ru = find(u), rv = find(v);
if (ru != rv) parent[ru] = rv;
}
// 條件 1:弱連通(所有有邊節點屬於同一連通分量)
int comp = -1;
bool connected = true;
for (int i = 0; i < 26; i++) {
if (in[i] || out[i]) {
int r = find(i);
if (comp == -1) comp = r;
else if (comp != r) { connected = false; break; }
}
}
// 條件 2:度數平衡
int start = 0, end = 0;
bool degree_ok = true;
for (int i = 0; i < 26; i++) {
if (out[i] == in[i] + 1) start++;
else if (in[i] == out[i] + 1) end++;
else if (in[i] != out[i]) { degree_ok = false; break; }
}
degree_ok = degree_ok && ((start == 0 && end == 0) || (start == 1 && end == 1));
printf("%s\n", (connected && degree_ok) ? "Possible" : "Impossible");
}
return 0;
}
| 步驟 | 說明 |
|---|---|
| 建圖 | 單詞 → 有向邊 (首字母→尾字母),統計入/出度。 |
| 連通性 | 並查集維護無向連通分量,僅檢查有邊節點是否同一分量。 |
| 度數條件 | 歐拉迴路:所有節點入=出;歐拉路徑:恰一起點(out=in+1)、一終點(in=out+1)。 |
| 複雜度 | O(N * L + 26) ≈ O(N),L 為平均單詞長度,極高效。 |
ab, bc, cd, de, fg, gh 兩條不相連鏈,度數雖平衡但無法串成單一序列。單詞鏈接問題是「有向圖歐拉路徑判定」的經典應用。掌握「入出度平衡 + 弱連通」兩大核心條件,即可在 O(N) 時間內精準求解。此模型廣泛應用於基因組裝(De Bruijn 圖)、郵遞員路徑規劃、電路板走線等領域。建議進一步學習 Hierholzer 演算法實際構造歐拉路徑序列。