給定 n (3≤n≤1000) 個單詞,每個單詞視為一條有向邊:從首字母指向尾字母。求一條經過所有邊恰好一次的路徑(歐拉路徑),並使拼接後的單詞序列字典序最小。不存在輸出 "***"。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
struct Edge { int u, v; string word; } edges[1100];
bool used[1100];
int in[27], out[27], fa[27], path[1100], top;
bool has[27];
int find(int x) { return fa[x] ? fa[x]=find(fa[x]) : x; }
bool checkEuler() {
// 連通性
int root = 0;
for (int i = 1; i <= 26; i++) if (has[i]) {
if (!root) root = find(i);
else if (find(i) != root) return false;
}
// 度數條件
int start = 0, cnt = 0;
for (int i = 1; i <= 26; i++) if (has[i]) {
if (in[i] != out[i]) {
if (abs(in[i] - out[i]) > 1) return false;
cnt++;
if (out[i] > in[i]) start = i;
}
}
if (cnt == 0) { // 回路,選最小字母作為起點
for (int i = 1; i <= 26; i++) if (has[i]) { start = i; break; }
} else if (cnt != 2) return false;
// 將 start 存入全局變量供 DFS 使用
return true;
}
void dfs(int u) {
for (int i = 1; i <= n; i++) if (!used[i] && edges[i].u == u) {
used[i] = true;
dfs(edges[i].v);
path[++top] = i;
}
}
int main() {
int T; cin >> T;
while (T--) {
cin >> n;
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(fa, 0, sizeof(fa));
memset(has, 0, sizeof(has));
for (int i = 1; i <= n; i++) {
cin >> edges[i].word;
edges[i].u = edges[i].word[0] - 'a' + 1;
edges[i].v = edges[i].word.back() - 'a' + 1;
has[edges[i].u] = has[edges[i].v] = true;
int fu = find(edges[i].u), fv = find(edges[i].v);
if (fu != fv) fa[fu] = fv;
out[edges[i].u]++; in[edges[i].v]++;
}
sort(edges+1, edges+n+1, [](const Edge& a, const Edge& b){ return a.word < b.word; });
if (!checkEuler()) { puts("***"); continue; }
top = 0; memset(used, 0, sizeof(used));
dfs(start);
for (int i = top; i >= 2; i--) cout << edges[path[i]].word << '.';
cout << edges[path[1]].word << endl;
}
return 0;
}