百度2017年暑期实习生笔试题——单词接龙

来源:互联网 发布:华润燃气oa软件 编辑:程序博客网 时间:2024/05/16 07:01

题目来源:[编程题]单词接龙

  这道题同时也是2016中兴捧月蓝剑之路挑战赛初赛模拟测试题,问题描述如下图:

 

  测试用例如下图:

 

  根据上面的TestCase 2的结果我们可以知道,对于输入单词的顺序我们是可以调整的,也就是说无论怎么调整单词的顺序,只要能形成接龙即可

  可能有人会想到通过调整单词的顺序,比如按首字母排序或按尾字母排序的方式对输入进行重新排序再依次检查首尾是否可以形成接龙,这样就掉入了本题的第一个陷阱。事实上,在本题中26个英文字母是没有所谓的先后顺序的,因为我们要判断的是这些单词能否形成一条接龙,即使你的首字母是a而我的首字母是z,但是你的单词是abc中而我的是单词zxa,你的单词仍然排在我的后面。

  这里有人可能已经看出来了,如果把每个单词看成一条有向边,首尾字母看成边上的起点和终点,问题就转化成了判断有向图中是否可以表示为一条(不闭合的)欧拉路径或欧拉回路的一笔画问题。

  根据有向图中欧拉路径的判定定理

  一个连通的有向图可以表示为一条从起点到终点的(不闭合的)欧拉路径的充要条件是: 起点的出度比入度多1, 终点的入度比出度多1,而其它顶点的入度和出度都相等。

  一个连通的有向图可以表示为一条欧拉回路的充要条件是:每个顶点的入度和出度都相等

   

  注意到上面我对连通的三个字进行了加粗,目的是强调不要忘记判断有向图的连通性(这里只要满足弱连通即可),对于百度那道笔试题网上很多人只考虑到统计点的入度与出度来判断是否能形成接龙,而没有检验有向图的连通性,虽然这样的代码能通过牛客OJ上所有的测试用例,但实际上对于aba,cdc,efe这样的输入却会得到错误的输出"Yes",原因就是这些单词形成的有向图是不连通的。这也从侧面说明了牛客OJ上用例设计的不够全面。

  中兴的比赛中官方已经为我们实现了主程序,只需要我们自己实现WordListOrder类的canArrangeWords方法即可,这里为了保证程序实现的完整性,我们以百度2017年暑期实习生笔试题为准,输入时首先给出单词的个数,再依次输入每个单词,输出"Yes"或"No",Java实现的源码如下:

  

复制代码
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;    }}
复制代码

 

  感谢@p101712911指出原程序中存在的bug,先前我把问题抽象成了无向图的一笔画问题,忽略了单词不能倒过来形成接龙的事实,对于某些特殊的输入比如:ca、ab、cb会得到错误的结果"Yes",改正后的程序把问题转化成有向图的一笔画问题并解决。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 耐吉结婚照 男扮女装去猎艳 男扮石油大亨囚禁10女模生7子 男儿本色qvod 男孩被体罚后截瘫 男科医院65856606 男模时代广场裸奔 男拧有噶坏 男女发现山洞闪金光 进一看兴奋极了 男女发现山洞闪金光进一看兴奋极了 男女警长假扮夫妻 男女蒲典wet123 男女疑因车震冲入河中 男女这里长一寸竟可增寿十年 男人婚前没有这经验抬不起头来 男生猛扇回击老师 男生喜欢女仆装小说 男士去眼袋费用 男童遭飞镖扎左眼 男员工被开除党籍 男子草丛发现异物 捅破后被吓坏了 男子车震被家人堵车内 男子发现山洞闪金光 进一看兴奋极了 男子韩币骗晕劫匪 男子患病体内钢钉 男子靠手机挡子弹 男子拦婚车要钱被打 男子拿祖传宝刀做抵押 知道结果后十分后悔 男子掐脖劫持婴儿 男子杀变性人伴侣 男子深夜按摩猝死 男子生日被闪电击 男子涂鸡血自导绑架案 男子玩浪漫高楼垂降 男子维修管道 竟发现骇人一幕 男子维修管道 这一幕让人头皮发麻 男子寻宝捡旧手提箱 里面东西惊动全城 男子烟瘾发作抢劫 男子岩穴住25年 男子意外获藏宝图 挖出大宝贝 男子雨中殴打怀孕妻子