在牛客网上碰到的题目,可以用查并集来做,遂记录之。
链接:https://www.nowcoder.com/questionTerminal/b1303e827e7f4df4a816598d008bbe72
经过深思熟虑之后,小贱君打算去M国闯一闯,那是一个古老的东方国度,传说有很多高阶魔法师,他想成为一名伟大的魔法师,将来征服星辰大海。
经过千辛万苦,小贱君终于来到了M国,不幸的是刚进城门小贱君就被M国的守城士兵困在了一种叫做“困兽之斗”的阵法之中。
士兵对小贱君说:“看到漂浮在你身边的宝石了吗?彩虹连接的两颗宝石可以任意交换位置,你需要通过一系列交换后使得宝石组成的字符串的字典序最小。若不能破阵,那还是请回吧!”
小贱君观察了一下周围的宝石,只见每颗宝石上标有一个小写字母,而且有一些宝石上通过彩虹与其他宝石相连。
琢磨了半天,他终于搞懂了这个阵法的意思:
若宝石系列为:dcba
其中有两道彩虹,分别是(0,1),(1,2),代表第一个位置上的宝石可以和第二个位置上的宝石互换,第二个位置上的宝石可以和第三个位置上的宝石互换,最终可以得到字典序最小的宝石系列:bcda。
作为小贱君的死党,你有什么方法帮助他破阵吗?
输入描述:
输入包含多组测试数据。 对于每组测试数据: 字符串s --- 代表宝石序列 n --- 代表有n条彩虹 接下来n行,每行两个数ai,bi --- 表示ai和bi由一条彩虹相连。 保证: 1<=s的长度<=10000 1<=n<=10000 且输入数据均合法。
输出描述:
对于每组数据,输出一个字符串
输入
dcba
2
0 1
1 2
hellonowcoder
4
0 1
1 4
2 5
2 3
输出
bcda
ehllonowcoder
首先想到的一种方法是:使用
ArrayList<HashSet<Integer>> lists = new ArrayList<HashSet<Integer>>();
把每个组的数据分别放到set里面,外面再套个list。其中需要用到查找。这个方法不能通过,创建了太多的 集合,导致内存超限。那么就换种方法,不用这么额外的对象存每组情况。
假设一个数组为:0,1,2,3,4,5,6,7,8,9 . 分组的操作为[0,1] [1,3] [3,9] [2,5] [2,3] 。
分完组后为{0,1,2,3,5,9} {4} {6} {7} {8} 。
假设另外建一个数组初始化为:{0,1,2,3,4,5,6,7,8,9 } 表示每个数字各为一组,如1 ,这个数字表示为一组,祖先即为自己。现在需要把同祖先的值变成一样,如0,1 是一组,把1位置也标为0,表示1的祖先也是0,即为一组了。下面来看看怎么做:
先给出一个查找祖先的方法:如果当前点r和pre[r]相等代表就是初始化的情况,否则把他的上一个结点拿出来再找祖先结点,找到最后一个即是当前结点的祖先结点,然后把pre[x] = r 指向祖先结点。
private static int find(int[] pre, int x) {
int r = x;
while (pre[r]!=r){
r = pre[r];
}
pre[x] = r;
return r;
}
现在给定一个[0,1]看看怎么添加成一组的,首先找0的祖先x=0,再找1的祖先y=0,这里为了统一把小的值作为祖先,把1位置指向的祖先变成0的祖先。如果是[1,3],首先找1的祖先x=0,再找3的祖先y=3,把3位置指向的祖先变成0的祖先。这一样做完一遍以后就能把相同组的值置为一样。
int x = find(pre,ai[i]);
int y = find(pre,bi[i]);
if (x<y){
pre[y] = pre[x];
}else {
pre[x] = pre[y];
}
但是这个有个问题:上面 0,1,2,3,4,5,6,7,8,9 . 分组的操作为[0,1] [1,3] [3,9] [2,5] [2,3] 。做完后pre数组为:
0,0,0,0,4,2,6,7,8,0 本应该5的位置也为0,但是没有置为0,因为操作的顺序原因,所以可以遍历一遍该数组重置一下不正确的值:
for (int i = 0; i < str.length(); i++) {
find(pre,i);
}
import java.util.Scanner;
/**
* Created by Administrator on 2017/4/19.
* 模拟 困兽之斗 使用并查集
* 查找(a,b)两个数的 跟结点,如果pre[a] < pre[b],就把b的根结点置为a的根结点
*
* 最后遍历的时候又找下了两个数的根结点,为什么还要再找下:因为碰见这种情况
* hellonowcoder
5
0 1
1 3
3 11
2 5
2 3
0 1 3 11 都为0, 2 5 都为2 , 最后一个(2,3)得到是 2 3 都为0. 然后就结束了,
但是这里的 5 对应的是2,不是0,所以再找一次也能得到5对应2.
*
*/
public class Moni20_2 {
public static void main(String []args){
Scanner in = new Scanner(System.in);
while (in.hasNext()){
String str = in.next();
int n = in.nextInt();
int[] ai = new int[n];
int[] bi = new int[n];
for (int i = 0; i < n; i++) {
ai[i] = in.nextInt();
bi[i] = in.nextInt();
}
String a = getRe(str,ai,bi);
System.out.println(a);
}
}
private static String getRe(String str, int[] ai, int[] bi) {
char[] arr = str.toCharArray();
int[] pre = new int[str.length()];
for (int i = 0; i < str.length(); i++) {
pre[i] = i;
}
//来一对数怎么把同一个组的关系添加进去
for (int i = 0; i < ai.length; i++) {
int x = find(pre,ai[i]);
int y = find(pre,bi[i]);
if (x
arr[j]){
char c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
}
}
return new String(arr);
}
//查找方法,把找到的祖先,变成当前数组pre的值,pre[x] = r;
private static int find(int[] pre, int x) {
int r = x;
while (pre[r]!=r){
r = pre[r];
}
pre[x] = r;
return r;
}
}
/**
*
hellonowcoder
5
0 1
1 3
3 11
2 5
2 3
*/