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

139 阅读1分钟

题目描述

给你一个字符串 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]
  }
}