[路飞]_LeetCode_交换字符串中的元素

163 阅读1分钟

「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

题目

给你一个字符串 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 中只含有小写英文字母

来源:力扣(LeetCode)leetcode-cn.com/problems/sm…

解题思路

可以交换的字符下标是一种连通关系,连通性关系都可以用并查集来解决。

处理过程:

  • 初始化并查集,节点个数是字符串长度;
  • 把能够交换的位置合并到一个集合当中;
  • 通过小顶堆将同一个集合的字符排序;
  • 最后循环获取小顶堆的顶部元素,合并成结果;

image.png

代码实现

var smallestStringWithSwaps = function (s, pairs) {
    const len = s.length
    //初始化并查集,节点个数是字符串长度
    const u = new UnionSet(len)

    //把能够交换的位置合并到一个集合当中
    for (const [a, b] of pairs) {
        u.merge(a, b)
    }

    //初始化 len 个最小优先队列,将同一个集合的字符排序
    const h = new Array(len).fill(0).map(() => new MinPriorityQueue())
    for (let i = 0; i < len; i++) {
        h[u.get(i)].enqueue(s[i], s[i].charCodeAt())
    }

    //合并结果
    let ans = ''
    for (let i = 0; i < len; i++) {
        //根据字符下标获取小顶堆,取堆顶字符拼接结果
        ans += h[u.get(i)].front()['element']
        h[u.get(i)].dequeue()
    }

    return ans
};

class UnionSet {
    constructor(n) {
        //初始化父节点数组,每个节点的父节点默认为自己
        this.pa = new Array(n + 1).fill(0).map((item, index) => index)

        //初始化每棵树的节点数
        this.size = new Array(n + 1).fill(1)
    }

    get(x) {
        //查找x的父节点,并且完成路径优化
        //优化后,x的父节点指向所在树的根节点
        return this.pa[x] = this.pa[x] === x ? x : this.get(this.pa[x])
    }

    merge(a, b) {
        //找到a的根节点
        const ra = this.get(a)
        //找到b的根节点
        const rb = this.get(b)

        //如果a和b在一个集合中则不需要合并
        if (ra === rb) return

        //把节点总数小的集合合并到节点总数多的集合里
        //更新节点总数多的集合为 a和b之和
        if (this.size[ra] < this.size[rb]) {
            this.pa[ra] = rb
            this.size[rb] += this.size[ra]
        } else {
            this.pa[rb] = ra
            this.size[ra] += this.size[rb]
        }
    }
}

如有错误欢迎指出,欢迎一起讨论!