Description
You are given a string s, and an array of pairs of indices in the string pairs where pairs[i] = [a, b] indicates 2 indices(0-indexed) of the string.
You can swap the characters at any pair of indices in the given pairs any number of times.
Return the lexicographically smallest string that s can be changed to after using the swaps.
Examples
Example 1:
Input: s = “dcab”, pairs = [[0,3],[1,2]]
Output: “bacd”
Explaination:
Swap s[0] and s[3], s = “bcad”
Swap s[1] and s[2], s = “bacd”
Example 2:
Input: s = “dcab”, pairs = [[0,3],[1,2],[0,2]]
Output: “abcd”
Explaination:
Swap s[0] and s[3], s = “bcad”
Swap s[0] and s[2], s = “acbd”
Swap s[1] and s[2], s = “abcd”
Example 3:
Input: s = “cba”, pairs = [[0,1],[1,2]]
Output: “abc”
Explaination:
Swap s[0] and s[1], s = “bca”
Swap s[1] and s[2], s = “bac”
Swap s[0] and s[1], s = “abc”
Constraints:
1 <= s.length <= 1 0 5 10^5 105
0 <= pairs.length <= 1 0 5 10^5 105
0 <= pairs[i][0], pairs[i][1] < s.length
s only contains lower case English letters.
思路
我原本以为这道题的难点在于找规律看怎么找到最佳排序,没想到真正的难点在于对思路的实现。
这题的思路在于, s s s 可以通过 p a i r s pairs pairs 分成几个不相交的簇,举个例子一个长度为8的字符串,可以通过 [0, 3], [1, 4], [3, 4], [2, 5], [7, 5] 分为3簇:
- [0, 1, 3, 4]
- [2, 5, 7]
- [6]
每个簇里面的字符都可以通过 “change” 操作随意排列组合,所以就将大问题分解成了小问题:
- 通过 p a i r s pairs pairs 分簇 (分簇方法可参考 547)
- 对每个簇内的字符串进行排序
- 再重新填回对应位置
所以最开始的时候我想的是创建一个 <List<List<Integer>> 用来装簇,但说我 Memory Limit Exceeded,所以后面就用一个数组 parent[] 来装每个元素的祖先,可以通过不断向上迭代找到共同祖先,从而达到“簇”的效果。
但最tricky的是,需要用到PriorityQueue,不然只用parent会因为不断向上迭代查找导致timeLimitExceed
这里空间和时间的关系要拿捏清楚,合理时间换空间,空间换时间
代码
Memory Limit Exceeded
class Solution {
public void subSort(List<Integer> cluster, char[] ch){
char[] toSort = new char[cluster.size()];
for (int i = 0; i < cluster.size(); i++)
toSort[i] = ch[cluster.get(i)];
Collections.sort(cluster);
Arrays.sort(toSort);
for (int i = 0; i < cluster.size(); i++)
ch[cluster.get(i)] = toSort[i];
}
public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) {
List<List<Integer>> clusters = new ArrayList<>();
boolean visited[] = new boolean[s.length()];
int[][] isConnect = new int[s.length()][s.length()];
// define 邻接矩阵
for (List<Integer> pair: pairs){
isConnect[pair.get(0)][pair.get(1)] = 1;
isConnect[pair.get(1)][pair.get(0)] = 1;
}
for (int i = 0; i < s.length(); i++)
isConnect[i][i] = 1;
// calculate clusters
for (int i = 0; i < s.length(); i++){
if (visited[i])
continue;
List<Integer> cluster = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
tmp.add(i);
cluster.add(i);
visited[i] = true;
while (tmp.size() != 0){
int curr_start = tmp.get(0);
for (int j = 0; j < s.length(); j++){
if (!visited[j] && isConnect[curr_start][j] == 1){
visited[j] = true;
tmp.add(j);
cluster.add(j);
}
}
tmp.remove(0);
}
clusters.add(cluster);
}
// sort in clusters
char[] ch = s.toCharArray();
for (List<Integer> cluster: clusters){
subSort(cluster, ch);
}
return String.valueOf(ch);
}
}
Time Limit Exceeded
class Solution {
int[] parent;
public String subSort(int pos, int target, String s){
char[] tmp = s.toCharArray();
List<Character> preSort = new ArrayList<>();
List<Integer> position = new ArrayList<>();
for (int i = pos; i < parent.length; i++){
if (findAnsestor(i) == target){
preSort.add(tmp[i]);
position.add(i);
}
}
Collections.sort(preSort);
for (int i = 0; i < position.size(); i++){
tmp[position.get(i)] = preSort.get(i);
}
return String.valueOf(tmp);
}
public int findAnsestor(int num){
while (parent[num] != num)
num = parent[num];
return num;
}
public void updateParent(int num1, int num2){
int parent1 = findAnsestor(num1);
int parent2 = findAnsestor(num2);
int par = Math.max(parent1, parent2);
parent[num1] = par;
parent[num2] = par;
parent[parent1] = par;
parent[parent2] = par;
}
public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) {
parent = new int[s.length()];
for (int i = 0; i< s.length(); i++)
parent[i] = i;
for (List<Integer> pair: pairs){
updateParent(pair.get(0), pair.get(1));
}
for (int i = 0; i < s.length(); i++){
int par = findAnsestor(i);
if (par <= i)
continue;
s = subSort(i, par, s);
}
return s;
}
}
Final
class Solution {
public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) {
Union u = new Union(s.length());
for(List<Integer> pair: pairs) {
u.union(pair.get(0), pair.get(1));
}
Map<Integer, PriorityQueue<Character>> map = new HashMap<>();
for(int i = 0; i < s.length(); i++) {
int root = u.find(i);
map.putIfAbsent(root, new PriorityQueue<>());
map.get(root).add(s.charAt(i));
}
StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length(); i++) {
sb.append(map.get(u.find(i)).poll());
}
return sb.toString();
}
}
class Union {
private int[] parent;
public Union(int size) {
this.parent = new int[size];
for(int i = 0; i < size; i++) {
this.parent[i] = i;
}
}
public int find(int idx) {
while(parent[idx] != idx) {
idx = parent[idx];
}
return idx;
}
public void union(int a, int b) {
int x = find(a);
int y = find(b);
if(x > y)
parent[x] = y;
else
parent[y] = x;
}
}