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

139 阅读3分钟

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

记录 1 道算法题

交换字符串中的元素

leetcode-cn.com/problems/sm…


要求:给定一个字符串和一个数组 pairs,是一个二维数组,放着字符串中两个字母的下标。然后只要是二维数组中同一组的下标,可以进行任意次数的交换,最后返回同一组中升序的新字符串。

例如:

s = "dcab", pairs = [[0,3],[1,2]]

得到 "bacd"
s = "dcab", pairs = [[0,3],[1,2],[0,2]]

得到 "abcd"

首先我们确定在一个组里面的字母是肯定能实现升序的,而且每个组内只有两个字母。

通过找规律可以找到每次交换只能是两两交换,然后如果有一个共同下标,那就可以进行两次交换实现跨组交换。比如 [0, 3] [0, 2],则可以先让 [0,3] 交换,然后再 [0, 2] 交换,实现 2 和 3 的交换。

所以组和组之间是有关联的,那既然是集合合并的问题,肯定用并查集进行解题。

我们的目标是找到有共同下标的组,让他们联合起来,无论是多少个组,只要是有共同下标,我们都可以通过多次交换实现多个组内的全替换,直到全是升序的。

比如 [0,3],[1,2],[0,2],我们可以得出 0 1 2 3 是相连的,所以可以进行多次排序实现 0 1 2 3 的升序。

使用并查集进行分组之后,我们还需要收集哪些字母是在同一个组里的,进行分组的时候并没有处理字母,因此要遍历一遍并查集列表。如何存储就比较考究,因为首先我们肯定需要把相连的字母放到一起,同时由于 pairs 里面提供的下标并不是相邻的,可能是中间跳开,镂空。所以对相连的字母排序之后,还要插入回对应的下标。所以我们也需要知道每一个字母对应的下标是什么。

所以我们可以用对象嵌套对象的方式进行存储,最外层对象是不同的相连字母的大集合,里面则是以字母下标为 key,字母为 value 的对象。

然后我们就可以通过 Object.keysObject.values 分别得到他们。

实现

    function smallestStringWithSwaps(s, pairs) {
        const len = s.length
        const parent = Array.from(new Array(len), (_, i) => i)
        // 并查集分组,只需要分组就不需要等级高合并等级低
        for (let [x, y] of pairs) {
            x = find(parent, x)
            y = find(parent, y)
            
            if (x === y) continue
            parent[y] = x
        }
        
        const map = {}
        // 收集相连的字母与下标
        // 结构 { '1': { '2': 'a', '3': 'c' } }
        for(let i = 0; i < len; i++) {
            // a 是并查集找到的根节点,代表同一组
            const a = find(parent, i)
            
            if (!map[a]) {
                map[a] = {}
            }
            const letter = s[i]
            map[a][i] = letter
        }
        
        const res = new Array(len)
        // 得到里面一个个对象
        const collection = Object.values(map)
        // 重新排序字母,并按下标插入,
        // 因为收集的时候是下标的升序排列,
        // 所以不用担心一会遍历时对不上。
        for(let item of collection) {
            const i = Object.keys(item)
            const letters = Object.values(item)
            
            letters.sort((a, b) => a.charCodeAt() - b.charCodeAt())
            letters.forEach((letter, ii) => {
                // i[ii] 才是对应的下标
                res[i[ii]] = letter
            })
        }
        
        return res.join('')
    }
    
    function find(parent, i) {
        if (parent[i] !== i) {
            parent[i] = find(parent, parent[i])
        }
        
        return parent[i]
    }

结束