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

2 years ago
#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;
}