题目

给你一个字符串 s,以及该字符串中的一些「索引对」数组 pairs,其中 pairs[i] = [a, b] 表示字符串中的两个索引(编号从 0 开始)。

你可以 任意多次交换 在 pairs 中任意一对索引处的字符。

返回在经过若干次交换后,s 可以变成的按字典序最小的字符串。

示例 1:

输入:s = “dcab”, pairs = [[0,3],[1,2]]
输出:”bacd”
解释:
交换 s[0] 和 s[3], s = “bcad”
交换 s[1] 和 s[2], s = “bacd”

示例 2:

输入:s = “dcab”, pairs = [[0,3],[1,2],[0,2]]
输出:”abcd”
解释:
交换 s[0] 和 s[3], s = “bcad”
交换 s[0] 和 s[2], s = “acbd”
交换 s[1] 和 s[2], s = “abcd”

示例 3:

输入:s = “cba”, pairs = [[0,1],[1,2]]
输出:”abc”
解释:
交换 s[0] 和 s[1], s = “bca”
交换 s[1] 和 s[2], s = “bac”
交换 s[0] 和 s[1], s = “abc”

提示:

1 <= s.length <= 10^5
0 <= pairs.length <= 10^5
0 <= pairs[i][0], pairs[i][1] < s.length
s 中只含有小写英文字母

思路

随机到了之前做过的一个题,看来力扣的随机机制改了,可以出现做过的题目了(之前从来没有遇到过…

距离上次做已经时隔一年多了,只有一点点印象。去年做没有完全消化,现在能想到解法还是很庆幸哈哈

大致的思路就是,对于相互可以交换的一组(说一组是因为可以由好多数组共同组成)字符,将其按照字母顺序排序,然后依次放在这些位置。

那具体如何实现?我们可以用并查集维护相互连通的字符,然后使用一个哈希表记录每个连通分量中的字符集合,哈希表的1202.交换字符串中的元素 - 图1为并查集的代表元,1202.交换字符串中的元素 - 图2为连通分量,因为要实现排序,所以可以用优先队列,也方便后面构造结果字符串(方便是指添加一个字符就将其删除)。最后构造字符串时,对于每个下标,查找其代表元对应的优先队列,1202.交换字符串中的元素 - 图3一个字符放在该下标。

具体实现细节见代码注释

代码

并查集+优先队列

  1. class Solution {
  2. public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) {
  3. int n = s.length();
  4. UnionFind uf = new UnionFind(n);
  5. for (List<Integer> pair : pairs) {
  6. // 并查集中存的是下标,不是字符,下标唯一而字符不唯一
  7. uf.union(pair.get(0), pair.get(1));
  8. }
  9. // key为并查集连通分量的代表元,value为该连通分量字符集合,使用优先队列方便排序
  10. // 对于s = "dcab", pairs = [[0,3],[1,2]],如果0和1分别是代表元,那么map大致就是{0:[b,d], 1:[a,c]}
  11. Map<Integer, PriorityQueue<Character>> map = new HashMap<>();
  12. for (int i = 0; i < n; i++) {
  13. map.computeIfAbsent(uf.find(i), k -> new PriorityQueue<>()).offer(s.charAt(i));
  14. }
  15. StringBuilder sb = new StringBuilder();
  16. for (int i = 0; i < n; i++) {
  17. // 使用优先队列,这里用掉一个字符,poll就行
  18. sb.append(map.get(uf.find(i)).poll());
  19. }
  20. return sb.toString();
  21. }
  22. }
  23. // 并查集类
  24. class UnionFind {
  25. int[] parent;
  26. public UnionFind(int n) {
  27. parent = new int[n];
  28. for (int i = 0; i < n; ++i) {
  29. parent[i] = i;
  30. }
  31. }
  32. // 查找p的代表元
  33. public int find(int p) {
  34. while (parent[p] != p) {
  35. parent[p] = parent[parent[p]];
  36. p = parent[p];
  37. }
  38. return p;
  39. }
  40. // 合并p和q
  41. public void union(int p, int q) {
  42. int proot = find(p);
  43. int qroot = find(q);
  44. if (proot == qroot) return;
  45. parent[proot] = qroot;
  46. }
  47. }