A catenym is a pair of words separated by a period such that the last letter of the first word is the same as the last letter of the second. For example, the following are catenyms:
A compound catenym is a sequence of three or more words separated by periods such that each adjacent pair of words forms a catenym. For example,
aloha.aloha.arachnid.dog.gopher.rat.tiger
Given a dictionary of lower case words, you are to find a compound catenym that contains each of the words exactly once.
dog.gopher
gopher.rat
rat.tiger
aloha.aloha
arachnid.dog
A compound catenym is a sequence of three or more words separated by periods such that each adjacent pair of words forms a catenym. For example,
aloha.aloha.arachnid.dog.gopher.rat.tiger
Given a dictionary of lower case words, you are to find a compound catenym that contains each of the words exactly once.
Input
The first line of standard input contains t, the number of test cases. Each test case begins with 3 <= n <= 1000 - the number of words in the dictionary. n distinct dictionary words follow; each word is a string of between 1 and 20 lowercase letters on a line by itself.
Output
For each test case, output a line giving the lexicographically least compound catenym that contains each dictionary word exactly once. Output "***" if there is no solution.
Sample Input
2 6 aloha arachnid dog gopher rat tiger 3 oak maple elm
Sample Output
aloha.arachnid.dog.gopher.rat.tiger ***
思路:
可以将每个单词看作是两点一边,就变成判断欧拉路径(回路)了,从连通性和出度入度两方面判断,然后打印欧拉路径即可,代码如下(注释
int in[30], out[30], ans[1005], start, fa[30], n, k; struct edge { int u, v, vis; string s; bool operator<(const edge &a) const { return s<a.s; } } Edge[1005]; void init() { memset(in, 0, sizeof(in)), memset(out, 0, sizeof(out)); memset(ans, 0, sizeof(ans)); k = 0; for(int i = 1; i <= 28; ++i) fa[i] = i; } // Find-Union int Find(int u) { if(fa[u] != u) return fa[u] = Find(fa[u]); return fa[u]; } void Union(int x, int y) { x = Find(x), y = Find(y); if(x != y) fa[x] = y; } // judge whether connected bool Connect() { int now = Find(start); for(int i = 1; i <= 26; ++i) if(in[i] || out[i]) if(Find(i) != now) return false; return true; } // judge the in and out and set the start bool check() { int t1 = 0, t2 = 0, i; for(i = 1; i <= 26; ++i) { if(in[i] == out[i]) continue; else if(in[i] == out[i] + 1) t1++; else if(in[i] == out[i] - 1) { t2++; start = i; } else break; } if(i == 27 && t1 == t2 && (t1 == 0 || t1 == 1)) { return true; } return false; } //print the euler path void dfs(int v) { for(int i = 1; i <= n; ++i) { if(Edge[i].vis == 0 && Edge[i].u == v) { Edge[i].vis = 1; dfs(Edge[i].v); ans[k++] = i; } } } int main() { ios::sync_with_stdio(false); int T; cin >> T; while(T--) { cin >> n; init(); for(int i = 1; i <= n; ++i) { int u, v; cin >> Edge[i].s; u = Edge[i].s[0] - 'a' + 1; v = Edge[i].s[Edge[i].s.size()-1] - 'a' + 1; in[v]++, out[u]++; Edge[i].u = u, Edge[i].v = v, Edge[i].vis = 0; Union(u, v); } sort(Edge+1, Edge+1+n); start = Edge[1].u; if(check() && Connect()) { dfs(start); for(int i = n-1; i >= 1; --i) cout << Edge[ans[i]].s << "."; cout << Edge[ans[0]].s << " "; } else { cout <<"*** "; } } return 0; }