欧拉路径
无向图
有向图
#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <deque>
#include <utility> // For std::pair
// 使用 using namespace std; 在竞赛中常见,但大型项目中不推荐
using namespace std;
// 常量定义
const int MAX_CHAR_VAL = 64; // 字符编码后的最大值 (0-63)
const int MAX_NODE_IDX = MAX_CHAR_VAL * 100 + MAX_CHAR_VAL + 1; // 节点索引最大值 + 1
// 将字符映射到整数 (a-z: 0-25, A-Z: 26-51, 0-9: 52-61)
// 你的原函数 f 映射到 0-25, 27-52, 54-63。为了简化,我们用连续的范围
int ctoi(char c) {
if (c >= 'a' && c <= 'z') return c - 'a';
if (c >= 'A' && c <= 'Z') return c - 'A' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
return -1; // 表示无效字符,尽管题目可能保证输入有效
}
// 将整数映射回字符
char itoc(int x) {
if (x >= 0 && x < 26) return 'a' + x;
if (x >= 26 && x < 52) return 'A' + (x - 26);
if (x >= 52 && x < 62) return '0' + (x - 52); // 0-9 是10个字符
return '?'; // 表示无效编码
}
// 节点用 pair<int, int> 表示其两个字符的编码
using Node = pair<int, int>;
// 邻接表,存储从当前节点出发可以到达的目标节点(也是 pair<int, int>)
// 原代码中 deque<node2> edge[114514]; node2 中的 vis 未使用,故简化
deque<Node> adj[MAX_NODE_IDX];
// 存储欧拉路径/回路的结果
stack<Node> path_stack;
// 度数信息
int in_deg[MAX_NODE_IDX]; // 入度
int out_deg[MAX_NODE_IDX]; // 出度
// p 数组在原代码中用于记录 out_deg - in_deg,这里可以用 bal (balance) 代替
int balance[MAX_NODE_IDX];
// Hierholzer 算法的 DFS 部分
// c1, c2 代表当前节点的两个字符编码
void find_euler_path(int c1, int c2) {
int u_idx = c1 * 100 + c2; // 当前节点的整数表示
// 遍历当前节点的所有出边
// 注意:这里修改了循环方式,原代码的 for + pop_front 有风险且低效
while (!adj[u_idx].empty()) {
Node next_node_comps = adj[u_idx].front();
adj[u_idx].pop_front(); // 取出并删除边,标记为已访问
find_euler_path(next_node_comps.first, next_node_comps.second);
}
// 当一个节点的所有出边都被访问后,将该节点入栈
path_stack.push({c1, c2});
}
int main() {
// 竞赛中加速IO
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n; // 输入的三字符序列数量
cin >> n;
if (n == 0) { // 特殊情况:没有边
cout << "YES\n\n"; // 或者只打印 YES 根据题目要求
return 0;
}
// 用于记录图中实际出现的第一个节点,作为欧拉回路的备选起点
Node first_graph_node = {-1, -1};
for (int i = 0; i < n; ++i) {
string s;
cin >> s; // 读取三个字符的字符串 "abc"
char char_a = s[0];
char char_b = s[1];
char char_c = s[2];
int code1 = ctoi(char_a);
int code2 = ctoi(char_b);
int code3 = ctoi(char_c);
// 节点 u: (code1, code2), 节点 v: (code2, code3)
// 边: u -> v
int u_idx = code1 * 100 + code2;
int v_idx = code2 * 100 + code3;
out_deg[u_idx]++;
in_deg[v_idx]++;
adj[u_idx].push_back({code2, code3}); // 存储目标节点的组成字符
if (first_graph_node.first == -1) {
first_graph_node = {code1, code2};
}
}
Node start_node = {-1, -1}; // DFS的起始节点
int start_candidates = 0; // 潜在路径起点数量 (out_deg - in_deg == 1)
int end_candidates = 0; // 潜在路径终点数量 (in_deg - out_deg == 1)
bool possible = true;
// 遍历所有可能的节点编码,检查度数条件
// 字符编码最大为 ctoi() 返回的最大值,比如 61
// 节点由两个字符编码组成
for (int c1_code = 0; c1_code < MAX_CHAR_VAL; ++c1_code) {
for (int c2_code = 0; c2_code < MAX_CHAR_VAL; ++c2_code) {
int current_node_idx = c1_code * 100 + c2_code;
// 只考虑在图中实际存在的节点 (至少有一条出边或入边)
if (out_deg[current_node_idx] == 0 && in_deg[current_node_idx] == 0) {
continue;
}
// 更新 balance 数组
balance[current_node_idx] = out_deg[current_node_idx] - in_deg[current_node_idx];
if (balance[current_node_idx] == 1) { // 欧拉路径的起点
start_candidates++;
start_node = {c1_code, c2_code};
} else if (balance[current_node_idx] == -1) { // 欧拉路径的终点
end_candidates++;
} else if (balance[current_node_idx] != 0) { // 度数不平衡,且不是起点/终点
possible = false;
break;
}
// 如果是欧拉回路 (balance == 0),且还没有设置起点,
// 并且该节点有出度,则可以作为回路的起点
if (start_node.first == -1 && out_deg[current_node_idx] > 0) {
// 这一步是为了确保如果图是欧拉回路,我们有一个起点
// first_graph_node 在读输入时已经记录了第一个遇到的 (有出度的) 节点,更可靠
}
}
if (!possible) break;
}
if (!possible) {
cout << "NO\n";
return 0;
}
// 判断欧拉路径/回路的条件
if (start_candidates == 0 && end_candidates == 0) {
// 可能是欧拉回路,或者图中没有满足条件的路径起点/终点
// 如果是欧拉回路,从图中任意一个有出度的节点开始
// first_graph_node 记录了第一个遇到的 (c1,c2) 节点,可以作为回路起点
if (first_graph_node.first != -1) { // 确保图不是空的
start_node = first_graph_node;
} else if (n > 0) { // 有边但找不到任何有效节点(理论上不应发生,除非图是空的或 ctoi 映射问题)
cout << "NO\n";
return 0;
}
// 如果 n=0,已在开头处理
} else if (start_candidates == 1 && end_candidates == 1) {
// 确定是欧拉路径,start_node 已经被设为起点
} else {
// 不满足欧拉路径或回路的度数条件
cout << "NO\n";
return 0;
}
// 如果有边输入 (n > 0),但没有找到合适的起点 (例如图不连通或度数条件不满足)
if (n > 0 && start_node.first == -1) {
cout << "NO\n";
return 0;
}
// 对于 n>0 的情况,此时 start_node 必须有效
if (start_node.first != -1) { // 只有当图非空且找到起点时才DFS
find_euler_path(start_node.first, start_node.second);
}
// 检查结果:路径中节点数应为边数 + 1
if (path_stack.size() == n + 1) {
cout << "YES\n";
// 输出路径
// 第一个节点 (c1, c2) 输出 c1c2
// 后续节点 (cx, cy) 只输出 cy
Node current = path_stack.top();
path_stack.pop();
cout << itoc(current.first) << itoc(current.second);
while (!path_stack.empty()) {
current = path_stack.top();
path_stack.pop();
cout << itoc(current.second); // 只输出第二个字符
}
cout << "\n";
} else {
cout << "NO\n";
}
return 0;
}