题目描述
武林内纷乱不断,各地都自立门派,门派的人为了识别门内弟子,给了每个人一块令牌,这个令牌有
个神奇的地方,门派内的弟子的令牌不一定相同,下面是他的识别规则:
每个人的令牌都是一串数字,如果两个人的令牌有相似的地方,即有相同的数字,那就属于同一个门
派,特别的,如果两个人没有相同的数字,但是这个两个人都和另一个人有相同的数字,那么这三个
人同属一个门派,现在有一个任务,给你n个令牌,让你认出有多少个门派
例如
3
13579
2468
12
这里答案应该是1,因为第一个人和第二个人同时和第三个人有关系
输入描述:
第一行 输入一个t,代表数据组数(1<=t<=10)
第二行 输入一个n,代表n块令牌 (1<=n<=1000)
下面n行,每行一个数字序列,(1<=len<=1000)
输出描述:
有t行,每行一个数,代表有这组数据有多少个门派
示例
输入
2
3
13579
2468
12
5
12
23
34
5
5678
输出
复制
1
2
思路:并查集
其实可以利用并查集的思想,同一个并查集里的元素应具有一些共同的性质,那么对于本体来说就是
具有共同的数字了。
那么如何对两个串进行合并呢?
我们其实可以记录每个数字第一次出现在那个字符串中,字符串用编号0 - n-1表示,那么我们每输入
一个字符串,遍历这个字符串,如果这个数字之前还没有出现过,就让这个数字指向当前这个字符串
否则就可以通过之前的指向去找出两个字符串是不是在同一个并查集中,如果不在,那么就将两个并
查集合并即可
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
char s[N];
int father[N] , f[N];
void init()
{
memset(f , -1 , sizeof f);
for(int i = 0 ; i < n ; ++ i) father[i] = i;
}
int find(int i)
{
if(father[i] == i) return i;
else return father[i] = find(father[i]);
}
int main(void)
{
int T;
cin >> T;
while(T --)
{
scanf("%d",&n);
init();//初始化函数
for(int i = 0 ; i < n ; ++ i)
{
scanf("%s" , s);
for(int j = 0 ; s[j] ; ++ j)//遍历字符串的每一个字符
{
int k = s[j] - '0';//得到当前的数字是多少
if(f[k] == -1) f[k] = i;//如果当前的数字之前还未出现,就指向当前这个字符串
else//如果出现过
{
//i代表的是当前的字符串,f[k]代表的是第一次出现这个数字是哪个字符串
int fa = find(i) , fb = find(f[k]);
//如果这两个并查集不是一个并查集
if(fa != fb)
father[fb] = fa;
}
}
}
//统计结果
int res = 0;
for(int i = 0 ; i < n ; ++ i)
if(father[i] == i)
res ++;
printf("%d\n",res);
}
return 0;
}