拉姆刚开始学习英文单词,对单词排序很感兴趣。
如果给拉姆一组单词,他能够迅速确定是否可以将这些单词排列在一个列表中,使得该列表中任何单词的首字母与前一单词的为字母相同。
你能编写一个程序来帮助拉姆进行判断吗?
输入描述:
输入包含多组测试数据。 对于每组测试数据,第一行为一个正整数n,代表有n个单词。 然后有n个字符串,代表n个单词。 保证: 2<=n<=200,每个单词长度大于1且小于等于10,且所有单词都是由小写字母组成。
输出描述:
对于每组数据,输出"Yes"或"No"
输入例子:
3 abc cdefg ghijkl 4 abc cdef fghijk xyz
输出例子:
Yes No
这个题目刷了两个小时,用了递归调用法,发现复杂度太大了。
百度查找经验:http://www.cnblogs.com/eniac12/p/5584361.html
图论应该是可行的。
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
// TODO Auto-generated method stub
Scanner scan = new Scanner(System.in);
while(scan.hasNext())
{
int n = scan.nextInt();
String[] arr = new String[n];
for(int i = 0; i < n ; i++)
arr[i] = scan.next();
System.out.println(WordListOrder.canArrangeWords(n, arr));
}
scan.close();
}
}
class WordListOrder {
public static String canArrangeWords(int n, String[] arr)
{
// 26个英文字母看作26个点,用整数0-25来表示
int[][] directedGraph = new int [26][26];// 邻接矩阵表示有向图
int[] inDegree = new int [26]; // 顶点入度
int[] outDegree = new int [26]; // 顶点出度
boolean[] hasLetter = new boolean[26]; // 标记字母是否出现过
boolean hasEuler = true; // 有狭义欧拉路径或欧拉回路标志
for(int i = 0; i < n; i++)
{
String word = arr[i];
char firstLetter = word.charAt(0);
char lastLetter = word.charAt(word.length()-1);
outDegree[firstLetter - 'a']++;
inDegree[lastLetter - 'a']++;
directedGraph[firstLetter - 'a'][lastLetter - 'a'] = 1; // 有向图
hasLetter[firstLetter - 'a'] = true;
hasLetter[lastLetter - 'a'] = true;
}
int startNum = 0;
int endNum = 0;
for (int vertex = 0; vertex < 26; vertex++)
{
if(outDegree[vertex] - inDegree[vertex] == 1) // 起点
startNum++;
if(inDegree[vertex] - outDegree[vertex] == 1) // 终点
endNum++;
if(Math.abs(inDegree[vertex] - outDegree[vertex]) > 1)
{
hasEuler = false;
break;
}
}
boolean isEulerPath = (startNum == 1 && endNum == 1); // 这里指狭义上的欧拉路径,不包括欧拉回路
boolean isEulerCircuit = (startNum == 0 && endNum == 0);// 欧拉回路
if((!isEulerPath) && (!isEulerCircuit)) // 既不是欧拉路径也不是欧拉回路
hasEuler = false;
// 判断是否弱连通
int vertexNum = 0; // 点的数量
for(int letter = 0; letter < 26; letter++)
{
if(hasLetter[letter])
vertexNum++;
}
int firstWordFirstLetter = arr[0].charAt(0) - 'a';// 以第一个单词的首字母作为起点
hasEuler = hasEuler && isConnected(firstWordFirstLetter, vertexNum, directedGraph);
if(hasEuler)
return "Yes";
else
return "No";
}
// 判断有向图是否弱连通
public static boolean isConnected(int start, int vertexNum, int[][] directedGraph)
{
int[][] undirectedGraph = new int[26][26];
for(int i = 0; i < 26; i++) // 把有向图转换成无向图
{
for(int j = 0; j < 26; j++)
{
if(directedGraph[i][j] == 1)
{
undirectedGraph[i][j] = 1;
undirectedGraph[j][i] = 1;
}
}
}
Queue<Integer> queue = new LinkedList<Integer>();
boolean[] passedVertex = new boolean[26];
queue.offer(start);
// 从起点开始进行广度优先搜索,把路过的边都拆掉
while(!queue.isEmpty())
{
int currentVertex = queue.peek();
passedVertex[currentVertex] = true;
queue.poll();
for(int vertex = 0; vertex < 26; vertex++)
{
if(undirectedGraph[currentVertex][vertex] == 1 && passedVertex[vertex] == false)
{
undirectedGraph[currentVertex][vertex] = 0;
undirectedGraph[vertex][currentVertex] = 0;
queue.offer(vertex);
}
}
}
int passedVertexNum = 0;
for(int vertex = 0; vertex < 26; vertex++)
{
if(passedVertex[vertex])
passedVertexNum++;
}
// 遍历到所有的点,证明无向图是连通的
if(passedVertexNum == vertexNum)
return true;
else
return false;
}
}