最近碰到了一个比较有意思的算法题 —— 单次接龙,我尝试做了一下,虽然能解,但是时间复杂度让我很不满意,于是google了网友的解法,找到一个比较满意的算法 —— 链接,作者是用JAVA实现的,我在此将它转换为C++实现。
问题描述
拉姆刚开始学习英文单词,对单词排序很感兴趣。如果给拉姆一组单词,他能够迅速确定是否可以将这些单词排列在一个列表中,使得该列表中任何单词的首字母与前一单词的尾字母相同。你能编写一个程序来帮助拉姆进行判断吗?
函数接口
bool CanArrayWords(int n, string arr[])
{
...
}
暴力求解
我最先想到的方法是:首先,将输入字符串数组进行全排列,然后验证每一种排列直到找到一种排列符合“单次接龙”,或者遍历各种排列也找不到符合“单词接龙”的排列。
这种解法,是可以判断是否符合“单词接龙”的,但是,它的时间复杂度是 O(n!),极其糟糕!
欧拉路径
网友提供了一个更高效的算法(链接),它的核心思想就是:将每一个单次看成是有向图的一条边,首尾字母看作顶点,那么这个问题就转化为了判断一个有向图是否可以表示为一条欧拉路径(不闭合)或欧拉回路的一笔画问题。
欧拉路径的判断定理
一个连通的有向图可以表示为一条从起点到终点的(不闭合的)欧拉路径的充要条件是: 起点的出度比入度多1, 终点的入度比出度多1,而其它顶点的入度和出度都相等。
一个连通的有向图可以表示为一条欧拉回路的充要条件是:每个顶点的入度和出度都相等
C++代码实现如下:
bool CanArrayWords(int n, string arr[])
{
// 26个英文字母看作26个点,用整数0-25来表示
int directedGraph[26][26] = {}; // 邻接矩阵表示有向图
int inDegree[26] = {}; // 顶点入度
int outDegree[26] = {}; // 顶点出度
bool hasLetter[26] = {};