题意:给出用户名和对应的邮箱,合并使用公有邮箱的用户名。
思路:一开始看到题目就觉得跟并查集有关系,但是想了半天没想出来怎么用,于是又看了解析。。。
输入给的数据是每个用户名对应哪些邮箱,我们利用倒排索引可以得到每个邮箱对应哪些用户名:
alice@hihocoder.com: alice
alice@gmail.com: alice alicetest
bob@qq.com: bob
alice@qq.com: alicetest alice2016
注意这里我们为了描述方便使用的还是字符串,实际上这时程序处理的应该是映射后的整数。
然后用并查集将有相同邮箱的用户名合并起来,再输出即可。
代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<stack>
#include<queue>
#include<utility>
#include<vector>
#include<cmath>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 10010;
string name[MAXN];
int nametot; //记录用户名的个数
map<string, int> mp; //邮箱名到编号的映射
int mailtot; //记录邮箱名的个数
vector<int> vec[MAXN*10]; //存储邮箱对应的用户名
int N;
int tree[MAXN]; //并查集
bool vis[MAXN]; //输出时用来标记
int FindTree(int a)
{
int r = a;
while(r != tree[r]){
r = tree[r];
}
int p = a;
while(p != r){
tree[p] = r;
p = tree[p];
}
return r;
}
void Join(int a, int b)
{
int ar = FindTree(a);
int br = FindTree(b);
if(ar != br){
tree[ar] = br;
}
FindTree(a);
}
void Init()
{
nametot = 0;
mailtot = 0;
mp.clear();
for(int i=0; i<MAXN; i++){
vec[i].clear();
}
for(int i=0; i<N; i++){
tree[i] = i;
}
memset(vis, false, sizeof(vis));
}
int main()
{
//freopen("in.txt", "r", stdin);
while(scanf("%d", &N) == 1){
Init();
while(N--){
cin >> name[nametot++];
int n;
string mail;
scanf("%d", &n);
while(n--){
cin >> mail;
if(mp.find(mail) == mp.end()){ //给邮箱编号
mp[mail] = mailtot++;
}
vec[mp[mail]].push_back(nametot-1);
}
}
//并查集的处理
for(int i=0; i<mailtot; i++){
if(vec[i].size() > 1){
int first = FindTree(vec[i][0]);
for(int j=1; j<vec[i].size(); j++){
int sec = FindTree(vec[i][j]);
if(sec != first){
Join(sec, first);
}
}
}
}
//输出
bool ok = true;
while(ok){
ok = false;
bool flag = true;
int tmp;
for(int i=0; i<nametot; i++){
if(!vis[i] && flag){
flag = false;
tmp = FindTree(i);
cout << name[i];
ok = true;
vis[i] = true;
}
else if(!vis[i] && tmp==FindTree(i)){
cout << " " << name[i];
vis[i] = true;
}
}
cout << endl;
}
}
return 0;
}