「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」。
记录 1 道算法题
交换字符串中的元素
要求:给定一个字符串和一个数组 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.keys、Object.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]
}
结束