题目:原题链接(困难)

标签:贪心算法、树状数组

解法 时间复杂度 空间复杂度 执行用时
Ans 1 (Python) O ( N l o g N ) O(NlogN) O(NlogN) O ( N ) O(N) O(N) 824ms (55.23%)
Ans 2 (Python)
Ans 3 (Python)

解法一:

class RangeUpdateBIT:
    def __init__(self, n: int):
        self.n = n
        self._tree = [0] * (n + 1)

    @staticmethod
    def _lowbit(x):
        return x & (-x)

    def update(self, i: int, x: int):
        self.add(i, x - (self.query(i) - self.query(i - 1)))

    def add(self, i: int, x: int):
        while i <= self.n:
            self._tree[i] += x
            i += RangeUpdateBIT._lowbit(i)

    def range_add(self, l: int, r: int, x: int):
        self.add(l, x)
        self.add(r + 1, -x)

    def query(self, i: int) -> int:
        ans = 0
        while i > 0:
            ans += self._tree[i]
            i -= RangeUpdateBIT._lowbit(i)
        return ans


class Solution:
    def minInteger(self, num: str, k: int) -> str:
        size = len(num)
        nums = [int(ch) for ch in num]

        # 构造各个数字的出现位置列表
        count = [collections.deque() for _ in range(10)]
        for i, n in enumerate(nums):
            count[n].append(i)

        # 构造树状数组
        bit = RangeUpdateBIT(size)

        ans = []

        # 贪心算法生成结果
        now = 0  # 当前的最小数字
        while k:
            # 如果当前的最小数字已经用完,或最小的数字已经不能移动到当前的第一位,则向后继续寻找次小的数字
            while now < 10 and len(count[now]) == 0:
                now += 1

            # 如果已经用完所有的数字,则返回结果
            if now == 10:
                break

            # 计算当前值的最优选择
            idx = now
            while idx < 10 and (len(count[idx]) == 0 or count[idx][0] - bit.query(count[idx][0] + 1) > k):
                idx += 1

            # 计算需要消耗的步数
            need = count[idx][0] - bit.query(count[idx][0] + 1)

            # 将当前数字添加到结果中
            ans.append(idx)

            # 从剩余的步数中减去需要的步数
            k -= need

            #  将所有更靠后的数字的距离-1
            bit.range_add(count[idx][0] + 1, size, 1)

            # 从剩余的数字中移除当前使用的数字
            count[idx].popleft()

        # 处理还交换次数用完后剩余的情况
        while len(ans) < size:
            # 寻找当前下标的最小值
            now = min([i for i in range(10)], key=lambda x: count[x][0] if len(count[x]) > 0 else size)

            ans.append(now)
            count[now].popleft()

        # 整理格式并返回结果
        return "".join([str(n) for n in ans])