树状数组-频次数组-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