[编程题]单词接龙

拉姆刚开始学习英文单词,对单词排序很感兴趣。
如果给拉姆一组单词,他能够迅速确定是否可以将这些单词排列在一个列表中,使得该列表中任何单词的首字母与前一单词的为字母相同。

你能编写一个程序来帮助拉姆进行判断吗?

输入描述:

 

输入包含多组测试数据。 对于每组测试数据,第一行为一个正整数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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值