题目描述
给你一个字符串 s,以及该字符串中的一些「索引对」数组 pairs,其中 pairs[i] = [a, b] 表示字符串中的两个索引(编号从 0 开始)。
你可以 任意多次交换 在 pairs 中任意一对索引处的字符。
返回在经过若干次交换后,s 可以变成的按字典序最小的字符串。
解题思路
算法,数据结构
并查集,最小堆
并查集中记录第 index 个字符的联通情况
最小堆记录第 index 个字符所连接的所有字符,字符从小到达排序(字典序)
思路
首先,遍历所有的 pairs,对于 pairs[i]:
merge 它记录的两个 index
然后,再遍历字符串,对于每一个字符:
首先拿到并查集中,记录的当前字符串的分量 u.get(index),然后对应的最小堆里,extract 弹出一个元素
如果我们声明一个变量 ans 来记录结果,那么需要 ans += 我们弹出的字符串
代码
/**
* @param {string} s
* @param {number[][]} pairs
* @return {string}
*/
var smallestStringWithSwaps = function (s, pairs) {
const u = new UnionSet(s.length)
const hArr = new Array(s.length).fill(0).map((item) => {
return new MinHeap((a, b) => {
return a > b
})
})
pairs.forEach((item) => {
u.merge(item[0], item[1])
})
for (let i = 0; i < s.length; i++) {
const c = s[i]
hArr[u.get(i)].insert(c)
}
console.log(...hArr.map((item) => item.heap))
let ans = ""
for (let i = 0; i < hArr.length; i++) {
ans += hArr[u.get(i)].extract()
}
return ans
}
class UnionSet {
constructor(n) {
this.fa = []
for (let i = 0; i < n; i++) {
this.fa[i] = i
}
}
get(x) {
return (this.fa[x] = this.fa[x] === x ? x : this.get(this.fa[x]))
}
merge(a, b) {
this.fa[this.get(a)] = this.get(b)
}
}
class MinHeap {
constructor(compareFn) {
this.compareFn = compareFn
this.heap = []
}
getLeftIndex(index) {
return index * 2 + 1
}
getRightIndex(index) {
return index * 2 + 2
}
getParentIndex(index) {
return Math.floor((index - 1) / 2)
}
size() {
return this.heap.length
}
isEmpty() {
return this.heap.length === 0
}
swap(parent, index) {
const arr = this.heap
;[arr[parent], arr[index]] = [arr[index], arr[parent]]
}
insert(value) {
const index = this.heap.length
this.heap.push(value)
this.siftUp(index)
}
siftUp(index) {
let parent = this.getParentIndex(index)
while (index > 0 && this.compareFn(this.heap[parent], this.heap[index])) {
this.swap(parent, index)
index = parent
parent = this.getParentIndex(index)
}
}
extract() {
if (this.isEmpty()) return
if (this.heap.length === 1) return this.heap.pop()
const removedItem = this.heap[0]
this.heap[0] = this.heap.pop()
this.siftDown(0)
return removedItem
}
siftDown(index) {
let element = index
const left = this.getLeftIndex(index)
const right = this.getRightIndex(index)
if (
index < this.size() &&
this.compareFn(this.heap[element], this.heap[left])
) {
element = left
}
if (
index < this.size() &&
this.compareFn(this.heap[element], this.heap[right])
) {
element = right
}
if (element !== index) {
this.swap(element, index)
this.siftDown(element)
}
}
top() {
return this.heap[0]
}
}