困兽之斗

来源:互联网 发布:翻译官软件如何 编辑:程序博客网 时间:2024/04/28 21:50

在牛客网上碰到的题目,可以用查并集来做,遂记录之。

链接: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的长度<=100001<=n<=10000且输入数据均合法。



输出描述:

对于每组数据,输出一个字符串

示例1

输入

dcba20 11 2hellonowcoder40 11 42 52 3

输出

bcdaehllonowcoder

首先想到的一种方法是:使用

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 (xarr[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 */