树状数组-频次数组-1505. 最多 K 次交换相邻数位后得到的最小整数

问题描述

给你一个字符串 num 和一个整数 k 。其中,num 表示一个很大的整数,字符串中的每个字符依次对应整数上的各个 数位 。

你可以交换这个整数相邻数位的数字 最多 k 次。

请你返回你能得到的最小整数,并以字符串形式返回。

示例 1:

输入:num = "4321", k = 4
输出:"1342"
解释:4321 通过 4 次交换相邻数位得到最小整数的步骤如上图所示。
示例 2:

输入:num = "100", k = 1
输出:"010"
解释:输出可以包含前导 0 ,但输入保证不会有前导 0 。
示例 3:

输入:num = "36789", k = 1000
输出:"36789"
解释:不需要做任何交换。
示例 4:

输入:num = "22", k = 22
输出:"22"
示例 5:

输入:num = "9438957234785635408", k = 23
输出:"0345989723478563548"

提示:
1 <= num.length <= 30000
num 只包含 数字 且不含有 前导 0 。
1 <= k <= 10^9

问题求解

对于每一轮的移动,我们都希望能将最小的数字移动到最前面,但是我们需要去记录在当前数字之前有多少数字已经移动过了,需要去除这些数字,因此需要引入树状数组来记录移动过的数字个数。

class Solution:
    def minInteger(self, num: str, k: int) -> str:
        n = len(num)
        record = []
        for i in range(10):
            record.append(deque())
        for idx, ch in enumerate(num):
            record[int(ch)].append(idx)

        bit = [0] * (n + 1)

        def update(i, delta):
            while i < len(bit):
                bit[i] += delta
                i += (i & -i)
        
        def query(i):
            res = 0
            while i:
                res += bit[i]
                i -= (i & -i)
            return res
        
        res = ""
        used = [0] * n

        while k and len(res) < n:
            for i in range(10):
                if not record[i]: continue
                idx = record[i][0]
                cost = idx - query(idx + 1)
                if cost > k: continue
                k -= cost
                res += num[idx]
                used[idx] = 1
                update(idx + 1, 1)
                record[i].popleft()
                break
        for i in range(n):
            if not used[i]: res += num[i]
        return res
posted @ 2022-07-09 15:12  hyserendipity  阅读(38)  评论(0编辑  收藏  举报