数据结构与算法(四)树

1.Pre-Post-erous!

描述

We are all familiar with pre-order, in-order and post-order traversals of binary trees. A common problem in data structure classes is to find the pre-order traversal of a binary tree when given the in-order and post-order traversals. Alternatively, you can find the post-order traversal when given the in-order and pre-order. However, in general you cannot determine the in-order traversal of a tree when given its pre-order and post-order traversals. Consider the four binary trees below:

    a   a   a   a
   /   /     \   \
  b   b       b   b
 /     \     /     \
c       c   c       c


All of these trees have the same pre-order and post-order traversals. This phenomenon is not restricted to binary trees, but holds for general m-ary trees as well.

输入

Input will consist of multiple problem instances. Each instance will consist of a line of the form
m s1 s2
indicating that the trees are m-ary trees, s1 is the pre-order traversal and s2 is the post-order traversal.All traversal strings will consist of lowercase alphabetic characters. For all input instances, 1 <= m <= 20 and the length of s1 and s2 will be between 1 and 26 inclusive. If the length of s1 is k (which is the same as the length of s2, of course), the first k letters of the alphabet will be used in the strings. An input line of 0 will terminate the input.

输出

For each problem instance, you should output one line containing the number of possible trees which would result in the pre-order and post-order traversals for the instance. All output values will be within the range of a 32-bit signed integer. For each problem instance, you are guaranteed that there is at least one tree with the given pre-order and post-order traversals.

样例输入

2 abc cba
2 abc bca
10 abc bca
13 abejkcfghid jkebfghicda
0

样例输出

4
1
45
207352860

题意很清楚,要算m叉树根据前序和后序遍历的结果还原的可能数。首先要对前序后序遍历以及树的性质比较了解,m叉树是需要严格区分子树的顺序的,也就是说即使前面是空的也不能补位。题目很容易想到递归,那么考虑一个子问题:给了一个子树T的两个遍历序列,怎么求这个T的可能数呢?首先肯定是求出这个T的根节点下面有几棵子树,假设求得是k棵。又因为子树的分布需要满足一定的顺序,所以这一层的子树排布可能数为C(m,k)。假设第i棵子树(1<=i<=k)有Ni种可能性,则T的可能数即为N(T)=C(m,k)*N1*N2*...*Nk。这样即可递归求解。

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<string.h>
#include<queue>
#include<stack>
#include<algorithm>
#include<cmath>
#include<vector>
#define REP(i,a,b) for(int i=(a);i<=(b);++i)
#define PER(i,a,b) for(int i=(a);i>=(b);--i)
#define mk(a,b) make_pair(a,b)
#define ll long long
using namespace std;
int m;
int C(int m,int k){
    ll ans=1;
    REP(i,m-k+1,m)ans*=i;
    REP(i,1,k)ans/=i;
    return (int)ans;
}
int func(int len,char* s1,char* s2){
    if(!len)return 1;
    int root=0,k=0,ans=1;
    while(root<len){
        REP(i,root,len){
            if(s2[i]==s1[root]){
                k++;
                ans*=func(i-root,s1+root+1,s2+root);
                root=i+1;break;
            }
        }
    }
    return ans*=C(m,k);
}
int main(){
    char s1[27],s2[27];
    while(scanf("%d",&m)&&m){
        scanf("%s%s",s1,s2);
        int l=strlen(s1);
        printf("%d\n",func(l-1,s1+1,s2));
    }
}

2.虫子的生活

描述
Hopper 博士正在研究一种罕见种类的虫子的性行为。他假定虫子只表现为两种性别,并且虫子只与异性进行交互。在他的实验中,不同的虫子个体和虫子的交互行为很容易区分开来,因为这些虫子的背上都被标记有一些标号。

现在给定一系列的虫子的交互,现在让你判断实验的结果是否验证了他的关于没有同性恋的虫子的假设或者是否存在一些虫子之间的交互证明假设是错的。

输入

输入的第一行包含实验的组数。每组实验数据第一行是虫子的个数(至少1个,最多2000个) 和交互的次数 (最多1000000次) ,以空格间隔. 在下面的几行中,每次交互通过给出交互的两个虫子的标号来表示,标号之间以空格间隔。已知虫子从1开始连续编号。

输出

每组测试数据的输出为2行,第一行包含 "Scenario #i:", 其中 i 是实验数据组数的标号,从1开始,第二行为 "No suspicious bugs found!" 如果实验结果和博士的假设相符,或 "Suspicious bugs found!" 如果Hopper的博士的假设是错误的

样例输入

2
3 3
1 2
2 3
1 3
4 2
1 2
3 4

样例输出

Scenario #1:
Suspicious bugs found!

Scenario #2:
No suspicious bugs found!

提示

数据量比较大,建议使用scanf

进入数这一章之后题目真的是越来越难了。这道题是并查集的一个应用,但是结构很巧妙。每次输入的一对数是属于不同的集合的,这样就不能简单地合并。有两种方法,可以每一次合并的时候记录一下与父节点的关系,也可以多开一个数组记录性别之间的关系。我才用的是第二种方法,开了一个比原来的数组大一倍的数组,用x+n表示与x性别相反的虫子,相当于每次输入四个数,(x,x+n,y,y+n)需要合并的是(x,y+n)与(y,x+n)。这样写比起记录性别思路要清楚很多,只是时间消耗可能会更大一些。但是由于并查集本身很快,就算是时间开销很大也能过。

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<string.h>
#include<queue>
#include<stack>
#include<algorithm>
#include<cmath>
#include<vector>
#define REP(i,a,b) for(int i=(a);i<=(b);++i)
#define PER(i,a,b) for(int i=(a);i>=(b);--i)
#define mk(a,b) make_pair(a,b)
#define ll long long
using namespace std;
int bugs[4020];
int num,cnt;
int find(int x){
    int k=x;
    while(bugs[k]!=k)k=bugs[k];
    int root=k;k=x;int temp;
    while(bugs[k]!=root){
        temp=bugs[k];bugs[k]=root;k=temp;
    }//路经压缩
    return root;
}
void uni(int a,int b){
    int ra,rb;
    ra=find(a);rb=find(b);
    if(ra!=rb)bugs[ra]=rb;
}
int main(){
    int t;cin>>t;int ca=0;
    while(t--){
        ca++;
        scanf("%d%d",&num,&cnt);
        REP(i,1,2*num)bugs[i]=i;
        int flag=0;
        while(cnt--){
            int a,b;scanf("%d%d",&a,&b);
            if(flag)continue;
            if(find(a)==find(b)||find(a+num)==find(b+num)){flag=1;continue;}
            else {uni(a,b+num);uni(a+num,b);}
        }
        if(flag)printf("Scenario #%d:\nSuspicious bugs found!\n\n",ca);
        else printf("Scenario #%d:\nNo suspicious bugs found!\n\n",ca);
    }
    return 0;
}

3.Shortest Prefixes

描述

A prefix of a string is a substring starting at the beginning of the given string. The prefixes of "carbon" are: "c", "ca", "car", "carb", "carbo", and "carbon". Note that the empty string is not considered a prefix in this problem, but every non-empty string is considered to be a prefix of itself. In everyday language, we tend to abbreviate words by prefixes. For example, "carbohydrate" is commonly abbreviated by "carb". In this problem, given a set of words, you will find for each word the shortest prefix that uniquely identifies the word it represents. 

In the sample input below, "carbohydrate" can be abbreviated to "carboh", but it cannot be abbreviated to "carbo" (or anything shorter) because there are other words in the list that begin with "carbo". 

An exact match will override a prefix match. For example, the prefix "car" matches the given word "car" exactly. Therefore, it is understood without ambiguity that "car" is an abbreviation for "car" , not for "carriage" or any of the other words in the list that begins with "car". 

输入

The input contains at least two, but no more than 1000 lines. Each line contains one word consisting of 1 to 20 lower case letters. 

输出

The output contains the same number of lines as the input. Each line of the output contains the word from the corresponding line of the input, followed by one blank space, and the shortest prefix that uniquely (without ambiguity) identifies this word. 

样例输入

carbohydrate
cart
carburetor
caramel
caribou
carbonic
cartilage
carbon
carriage
carton
car
carbonate

样例输出

carbohydrate carboh
cart cart
carburetor carbu
caramel cara
caribou cari
carbonic carboni
cartilage carti
carbon carbon
carriage carr
carton carto
car car
carbonate carbona

 

4.Disk Tree

描述

Hacker Bill has accidentally lost all the information from his workstation's hard drive and he has no backup copies of its contents. He does not regret for the loss of the files themselves, but for the very nice and convenient directory structure that he had created and cherished during years of work. Fortunately, Bill has several copies of directory listings from his hard drive. Using those listings he was able to recover full paths (like "WINNT\SYSTEM32\CERTSRV\CERTCO~1\X86") for some directories. He put all of them in a file by writing each path he has found on a separate line. Your task is to write a program that will help Bill to restore his state of the art directory structure by providing nicely formatted directory tree.

输入

The first line of the input file contains single integer number N (1 <= N <= 500) that denotes a total number of distinct directory paths. Then N lines with directory paths follow. Each directory path occupies a single line and does not contain any spaces, including leading or trailing ones. No path exceeds 80 characters. Each path is listed once and consists of a number of directory names separated by a back slash ("\"). 

Each directory name consists of 1 to 8 uppercase letters, numbers, or the special characters from the following list: exclamation mark, number sign, dollar sign, percent sign, ampersand, apostrophe, opening and closing parenthesis, hyphen sign, commercial at, circumflex accent, underscore, grave accent, opening and closing curly bracket, and tilde ("!#$%&'()-@^_`{}~").

输出

Write to the output file the formatted directory tree. Each directory name shall be listed on its own line preceded by a number of spaces that indicate its depth in the directory hierarchy. The subdirectories shall be listed in lexicographic order immediately after their parent directories preceded by one more space than their parent directory. Top level directories shall have no spaces printed before their names and shall be listed in lexicographic order. See sample below for clarification of the output format.

样例输入

7
WINNT\SYSTEM32\CONFIG
GAMES
WINNT\DRIVERS
HOME
WIN\SOFT
GAMES\DRIVERS
WINNT\SYSTEM32\CERTSRV\CERTCO~1\X86

样例输出

GAMES
 DRIVERS
HOME
WIN
 SOFT
WINNT
 DRIVERS
 SYSTEM32
  CERTSRV
   CERTCO~1
    X86
  CONFIG
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<string>
#include<cstring>
#include<string.h>
using namespace std;
#define REP(i,a,b) for(int i=(a);i<=(b);++i)
#define PER(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define mk make_pair
#define MAX 20000
map<string,int> m[MAX];//构建森林,同时实现字典序排序
void pre(int idx,int level){
    for(map<string,int>::iterator i=m[idx].begin();i!=m[idx].end();++i){
        for(int k=0;k<level;++k)printf(" ");
        printf("%s\n",(i->first).c_str());//c_str返回当前string首字符的地址
        pre(i->second,level+1);
    }
}
int main(){
    char str[85];//存储所有文件名
    int word_init[45];//记录每个文件名的开头位置
    int idx;//节点编号
    int pnt=0;//开辟的map数
    string cur_str;//当前文件名
    int t;cin>>t;
    while(t--){
        scanf("%s",str);
        int l=strlen(str);
        int num=1;//文件数
        word_init[0]=0;
        REP(i,0,l-1){
            if(str[i]=='\\'){
                str[i]=0;word_init[num]=i+1;num++;
            }
        }
        idx=0;
        REP(i,0,num-1){
            cur_str=str+word_init[i];
            if(m[idx].find(cur_str)==m[idx].end()){
                pnt++;
                m[idx][cur_str]=pnt;
            }
            idx=m[idx][cur_str];
        }
    }
    pre(0,0);
    return 0;
}

建立一个森林,然后对森林中的每一棵树进行先根遍历,打印结点信息(包含缩进和文字)。题目要求根据字典顺序输出,所以要对输入的文本进行排序,这样在建立树的时候,结点的先根遍历顺序也是按照字典顺序的。使用map数组来实现这一功能。每一个map<string,int>表示某一节点的子节点全体。<string->文件名,int->子节点集在map数组中的下标>

思路来源:https://blog.youkuaiyun.com/ditian1027/article/details/21025385

5.A Bug's Life

描述

Background
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
Problem
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.

输入

The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.

输出

The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.

样例输入

2
3 3
1 2
2 3
1 3
4 2
1 2
3 4

样例输出

Scenario #1:
Suspicious bugs found!

Scenario #2:
No suspicious bugs found!

提示

Huge input,scanf is recommended.

6.食物链

描述

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

输入

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

输出

只有一个整数,表示假话的数目。

样例输入

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

样例输出

3

解析:和“虫子的生活”有点类似,但是这里是三个种类。第一种方法是开一个三倍的数组,每次输入时先假设i属于A,如果i吃j,则i+n与j为一类,同理,i+2n与j+n是一类,i与j+2n是一类,也就是一个输入相当于给了三个信息,每一次判断有没有错误都需要判断三次。第二种方法是并查集的同一类不以是同一类物种作为分类依据,而把能够判断是否属于同意物种的生物归为一类(这个思路很重要),只需每次find时递归改变每个节点与根节点的关系即可。

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<string>
#include<cstring>
#include<string.h>
using namespace std;
#define REP(i,a,b) for(int i=(a);i<=(b);++i)
#define PER(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define mk make_pair
#define MAX 50010
int father[MAX];//node
int rel[MAX];//relationship between x and "the root of x"
// rel[x]=0->root[x] is the same kind with x
//rel[x]=1->root[x] eats x, rel[x]=2-> x eats root[x]
int find(int x){
    if(x!=father[x]){
        int root=find(father[x]);
        rel[x]=(rel[x]+rel[father[x]])%3;
        father[x]=root;
    }
    return father[x];
}
int UnionAndJudge(int d,int x,int y){
    int rx=find(x);
    int ry=find(y);
    if(rx==ry){
        if((rel[y]-rel[x]+3)%3!=d)return 1;
        //此公式推导相当于先承认y是x的儿子则由x与x同类
        //有关系式:(rel[x]+d-rel[y])%3=0,变形,并考虑到rel[y]<rel[x]的情况可推出上式
        else return 0;
    }
    father[ry]=rx;
    rel[ry]=(rel[x]-rel[y]+d+3)%3;
    //这里仅将ry的父亲设为rx并没有改变ry的儿子们yi的rel[yi]
    //但是每次寻找yi的根节点时都会更新其与根节点的关系,所以这样做是合理的
    //上面rel关系式的推导同理
    return 0;
}
int main(){
    int ans=0;
    int n,k;
    int d,x,y;
    scanf("%d%d",&n,&k);
    REP(i,1,n){father[i]=i;rel[i]=0;}
    while(k--){
        scanf("%d%d%d",&d,&x,&y);
        if((x==y&&d==2)||x>n||y>n){ans++;continue;}
        if(UnionAndJudge(d-1,x,y))ans++;
    }
    printf("%d",ans);
    return 0;
}

7.物质分解记录

描述

对 物质分解记录 的结构进行统计分析。
例如:
给出一份 物质分解记录。
Material_1
{
Material_2
{
Material_3
                Material_4
Material_5
                {
                Material_6
                Material_7
                }
                Material_8
}
Material_9
Material_10
}
Material_11
{
Material_l3
Material_7
Material_2
{
Material_3
                Material_4
Material_5
                {
             Material_6
             Material_7
                }
                Material_8
}
Material_13
}

上述记录的含义是,Material_1分解为 Material_2、Material_9和Material_10,Material_2又分解为Material_3、Material_4、Material_5和Material_8。以此类推,大括号外书写特定物质名称,括号内表示此特定物质分解出来的子物质名称,每个子物质还可再分解。

现输入一个物质名称R,要求输出所有和物质R在记录中属于同一层次且位置在R之后的物质名称。
比如R=“Material_1” ,则应该输出“Material_11”;
比如R=“Material_9” ,则应该输出“Material_10”
如果R在代码中出现了多次,则以其第一次出现为准,即仅输出与第一次出现的R属于同一层次且位置在R之后的语句内容。
比如R=“Material_2” ,则应该输出
        Material_9
Material_10

输入

输入包含多组数据。第一行是物质分解记录的份数,仅用一个整数表示。从第二行开始,每组数据包括 物质分解记录 和 所需查找的物质R 两部分,物质分解记录样式如描述中所示,R的内容和物质分解记录之间有一行空行,下一份记录与上一个R之间有两行空行。
若输入!则表示输入结束。
为简单起见,物质分解记录中每一行的内容为“{”或者“}”或者一个物质名称,不会有其他情况(比如空行)出现。同时每行文字前不会有任何缩进。物质名称是英文字母、数字和下划线组成的字符串。

输出

对每组数据输出一行,如果R在记录中找到,则输出所有与R在同一层次且位置在R之后的物质名称,名称之间无需添加空格,紧密连接即可;否则输出No。若R是其所在层次中最后一个物质,则输出"",即输出一个空字符。

样例输入

3
Material_1
{
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_9
Material_10
}

Material_2


Material_1
{
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_9
Material_10
}
Material_11
{
Material_3
Material_7
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_13
}

Material_2


Material_1
{
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_9
Material_10
}

Material_20


!

样例输出

Material_9Material_10
Material_9Material_10
No

提示

读入数据时,需采用如下方式进行读取。
例:若要读取一行输入内容,则
cin.getline(line, lineSize, '\n');
sscanf(line, "%s", tmp);
其中line和tmp为数组指针,类型为char* ,linesize为line所指向的数组的规模,为int型。
所需读取的内容最终是存储在tmp数组中。之后如需对读取的内容进行操作,就对tmp进行操作即可,读到空行时tmp长度即为0。
采用其他方法读取可能会出现WA以及RE,TLE

解析:问题的关键是怎样由括号提供的信息建树。毫无疑问,读到“ { ”时应该下到儿子的关系去,但是读到“ } ”时呢?可以联想到括号匹配,使用一个栈记录父亲节点的信息,每次读到“ } ”则出栈一个父亲节点。之后只需要遍历寻找第一个符合条件的物质,然后回到他的父亲节点在此物质之后的兄弟节点。

 

8.树的转换

描述

我们都知道用“左儿子右兄弟”的方法可以将一棵一般的树转换为二叉树,如:

    0                             0
  / | \                          /
 1  2  3       ===>             1
   / \                           \
  4   5                           2
                                 / \
                                4   3
                                 \
                                  5

现在请你将一些一般的树用这种方法转换为二叉树,并输出转换前和转换后树的高度。

输入

输入包括多行,最后一行以一个#表示结束。
每行是一个由“u”和“d”组成的字符串,表示一棵树的深度优先搜索信息。比如,dudduduudu可以用来表示上文中的左树,因为搜索过程为:0 Down to 1 Up to 0 Down to 2 Down to 4 Up to 2 Down to 5 Up to 2 Up to 0 Down to 3 Up to 0。
你可以认为每棵树的结点数至少为2,并且不超过10000。

输出

对于每棵树,按如下格式输出转换前和转换后树的高度:
Tree t: h1 => h2
其中t是树的编号(从1开始),h1是转换前树的高度,h2是转换后树的高度。

样例输入

dudduduudu
ddddduuuuu
dddduduuuu
dddduuduuu
#

样例输出

Tree 1: 2 => 4
Tree 2: 5 => 5
Tree 3: 4 => 5
Tree 4: 4 => 4

解析:此问题可以转换为两个小问题,怎样由一颗树的dfs信息还原这棵树以及怎样由一棵树求出其二叉树的高度。第一个问题:较为简单,就是按照dfs的顺序还原这棵树。每次遇到d就深入一层,建立此节点的儿子,遇到u就反回上一层节点。第二个问题:稍难一些,是否可以不将树转换为二叉树就计算出转换后二叉树的高度呢?事实上是可以的,但是需要理解“左兄弟,右孩子”的建树方法。可以递归地更新二叉树的高度,每一个节点有一个与根节点的距离h,设其最深还可以深入h1则此结点对应的高度则为h+h1。要求h1则需要进入它的右兄弟和儿子们,这样递归可求出h1=max{hi,hi为右兄弟和儿子们还能下降的最大层数}。

9.宗教信仰

描述

世界上有许多宗教,你感兴趣的是你学校里的同学信仰多少种宗教。

你的学校有n名学生(0 < n <= 50000),你不太可能询问每个人的宗教信仰,因为他们不太愿意透露。但是当你同时找到2名学生,他们却愿意告诉你他们是否信仰同一宗教,你可以通过很多这样的询问估算学校里的宗教数目的上限。你可以认为每名学生只会信仰最多一种宗教。

 

输入

输入包括多组数据。
每组数据的第一行包括n和m,0 <= m <= n(n-1)/2,其后m行每行包括两个数字i和j,表示学生i和学生j信仰同一宗教,学生被标号为1至n。输入以一行 n = m = 0 作为结束。

输出

对于每组数据,先输出它的编号(从1开始),接着输出学生信仰的不同宗教的数目上限。

样例输入

10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0

样例输出

Case 1: 1
Case 2: 7

解析:并查集的简单应用。

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<string>
#include<cstring>
#include<string.h>
using namespace std;
#define REP(i,a,b) for(int i=(a);i<=(b);++i)
#define PER(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define mk make_pair
#define MAX 50010
int father[MAX];
int find(int a){
    int temp=a;
    while(father[temp]!=-1)temp=father[temp];
    int root=temp;temp=a;
    int tmp;
    while(father[temp]!=-1){tmp=father[temp];father[temp]=root;temp=tmp;}
    return root;
}
void uni(int a,int b){
    int ra=find(a),rb=find(b);
    if(ra==rb)return;
    else{
        father[rb]=ra;
    }
}
int main(){
    int n,m;int ca=0;
    while(cin>>n>>m&&(n||m)){
        ca++;
        REP(i,1,n)father[i]=-1;
        while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            uni(a,b);
        }
        int cnt=0;
        REP(i,1,n)if(father[i]==-1)cnt++;
        printf("Case %d: %d\n",ca,cnt);
    }
    return 0;
}

10.电话号码

描述

给你一些电话号码,请判断它们是否是一致的,即是否有某个电话是另一个电话的前缀。比如:

Emergency 911
Alice 97 625 999
Bob 91 12 54 26

在这个例子中,我们不可能拨通Bob的电话,因为Emergency的电话是它的前缀,当拨打Bob的电话时会先接通Emergency,所以这些电话号码不是一致的。

输入

第一行是一个整数t,1 ≤ t ≤ 40,表示测试数据的数目。
每个测试样例的第一行是一个整数n,1 ≤ n ≤ 10000,其后n行每行是一个不超过10位的电话号码。

输出

对于每个测试数据,如果是一致的输出“YES”,如果不是输出“NO”。

样例输入

2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

样例输出

NO
YES

解析:trie树,注意先插入长串后插入短串,和先短串后长串两种情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值