String swapLexOrder(String str, int[][] pairs) {
    Map<Integer, Set<Integer>> map = new HashMap<Integer, Set<Integer>>();
    
    for (int[] edge: pairs) {
        int u = edge[0];
        int v = edge[1];
        
        map.putIfAbsent(u, new HashSet<Integer>());
        map.putIfAbsent(v, new HashSet<Integer>());
        
        map.get(u).add(v);
        map.get(v).add(u);
    }
    
    // DFS and collect all connected components, order ascending
    List<TreeSet<Integer>> ccs = new ArrayList<TreeSet<Integer>>();
    Set<Integer> visited = new HashSet<Integer>();
    
    for (Integer u: map.keySet()) {
        if (!visited.contains(u)) {
            visited.add(u);
            TreeSet<Integer> current = new TreeSet<Integer>();
            current.add(u);
            dfs(map, u, visited, current);
            ccs.add(current);
        }
    }
    
    char[] ans = str.toCharArray();
    // Collect chars at connected component, sort des and populate output
    for (TreeSet<Integer> set : ccs) {
        List<Character> ordered = new ArrayList<Character>();
        Iterator iter = set.iterator();
        while (iter.hasNext()) {
            int index = (int)iter.next() - 1;
            ordered.add(str.charAt(index));
        }
        Collections.sort(ordered, Collections.reverseOrder());
        iter = set.iterator();
        int i = 0;
        while (iter.hasNext()) {
            ans[(int)iter.next() - 1] = ordered.get(i++);
        }
    }
    return new String(ans);
}

void dfs(Map<Integer, Set<Integer>> map, int u, Set<Integer> visited, TreeSet<Integer> current) {
    for (int v: map.get(u)) {
        if (!visited.contains(v)) {
            visited.add(v);
            current.add(v);
            dfs(map, v, visited, current);
        }
    }
}