You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
4.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int M = 1010; //字符串数量上限,可以理解为边数
const int N = 26; //a-z映射了26个数字,也就是图中结点的数字上限
vector<string> str; //输入的字符串数组
vector<int> path; //结点的搜索路径
int n; //字符串数量
bool st[M]; //是否使用
int fa[N]; //并查集数组
int in[N]; //入度
int out[N]; //出度
unordered_map<int, int> _map;//用来记录某个结点号是否出现过
//并查集,用来判断底图的连通性
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
//加入家族集合中
void join(int a, int b) {
int f1 = find(a), f2 = find(b);
if (f1 != f2)fa[f1] = f2;
}
/**
* 功能:深度优先搜索
* @param u 从哪个结点ID出发
* @param step 已经走了多少条边
*/
void dfs(int u, int step) {
//如果成功走了n条边则是成功的标识
if (step == n) {
//输出路径
for (int i = 0; i < path.size(); i++) {
cout << str[path[i]];
if (i < path.size() - 1) cout << '.'; //最后一个不输出.只有前面n-1个输出.
}
exit(0);
}
//遍历所有可能性,走多叉树,找出欧拉路径,1找到就直接退出2从start开始肯定有这两条保证了效率的提升
for (int i = 0; i < n; i++) {
//找到能和u结点匹配的下一个未使用过的字符串
if (!st[i] && str[i][0] - 'a' == u) {
path.push_back(i); //放入到路径中
st[i] = true; //标识为已使用
//开始尝试下一个字符串
dfs(str[i].back() - 'a', step + 1);
//回溯
st[i] = false; //未使用
path.pop_back(); //路径弹出
}
}
}
int main() {
//输入字符串
cin >> n;
string s;
for (int i = 0; i < n; i++) cin >> s, str.push_back(s);
// 排序确保搜索字典序最小
sort(str.begin(), str.end());
//并查集初始化,每个人都是自己的祖先
for (int i = 0; i < N; i++) fa[i] = i;
//检查一下是不是底图连通,采用并查集,通俗点说:就是看看是不是能全部连通在一起,如果不能,最后是多个家族,则不是欧拉图
for (int i = 0; i < n; i++) {
//每行是一个由 1 到 20 个"小写"字母组成的单词
int a = str[i][0] - 'a'; //首字母映射号,相当于结点编号
int b = str[i].back() - 'a'; //尾字母映射号,相当于结点编号
//转为数字,建立并查集之间的关系,这个字符映射数字用的很妙。
// join合并并查集
join(a, b);
//标识a和b都使用过
_map[a]++, _map[b]++;
//记录b的入度++
in[b]++;
//记录a的出度++
out[a]++;
//之所以记录出度和入度,是为了下一步的欧拉图二次检测
}
// 判断欧拉图的第一个要求:底图连通性
int cnt = 0; //家族的数量
for (int i = 0; i < N; i++) if (fa[i] == i && _map[i]) cnt++;//自己是自己的祖先,并且出现过
//并查集的家族个数大于1表示不可能是欧拉图
if (cnt > 1) {
cout << "***";
return 0;
}
int flag = 0; //出度与入度差是1的个数
int start = str[0][0] - 'a';//默认是第一个字符串的第一个字符对应的结点
// 判断欧拉图的第二个要求:出度与入度的数字关系
for (int i = 0; i < N; i++) {
//计算每个结点的出度与入度的差
int k = out[i] - in[i];
//出度与入度差大于1则肯定不是欧拉图
if (abs(k) > 1) {
cout << "***";
return 0;
}
//如果差是1那么需要检查是不是2个,2个才是一个入口点一个出口点
if (abs(k) == 1) {
//记录个数
flag++;
//如果出度比入度大1记录下起点是哪个结点
if (k == 1) start = i;
}
}
//如果不是0也不是2那么不是欧拉图
if (flag != 0 && flag != 2) {
cout << "***";
return 0;
}
//这时,肯定是有一条欧拉路径的了,找出这条欧拉路径
dfs(start, 0);
return 0;
}