题意:
给你一个字符串 num 和一个整数 k 。
其中,num 表示一个很大的整数,字符串中的每个字符依次对应整数上的各个 数位 。
你可以交换这个整数相邻数位的数字 最多 k 次。
请你返回你能得到的最小整数,并以字符串形式返回。
数据范围:
1 <= num.length <= 30000
num 只包含 数字 且不含有 前导 0 。
1 <= k <= 10^9
解法:
由于操作之后数位个数不会发生改变,
因此要求整数最小其实就是要求字典序最小.
字典序最小显然要先让最高位尽可能小,
对于答案的第一位(左边),从数字0开始枚举,判断是否能在k步之内移动一个最近的过来.
假设将pos位置上的数移动过来了,那么最高位到pos之间的所有数向右移动了一位,
那么对于每个字符s[i],移动之后的位置应该是:i+([i+1,n]移动到左边的字符数量).
([i+1,n]移动到左边的字符数量)可以用树状数组维护,具体做法是:
当pos位置的数被移动,令c[pos]]=1,
那么我们统计[i+1,n]中有多少个数移动到左边,
其实就是计算[i+1,n]中1的个数即区间和,可以用树状数组计算.
code:
struct BIT{
const static int maxm=3e4+5;
int c[maxm];
int lowbit(int i){
return i&-i;
}
void add(int i,int t){
while(i<maxm)c[i]+=t,i+=lowbit(i);
}
int ask(int i){
int ans=0;
while(i)ans+=c[i],i-=lowbit(i);
return ans;
}
void init(){
memset(c,0,sizeof c);
}
};
class Solution {
public:
BIT T;
string minInteger(string s, int k) {
T.init();
string ans;
int n=s.size();
vector<queue<int> >pos(10);
for(int i=0;i<n;i++){
pos[s[i]-'0'].push(i+1);
}
for(int i=1;i<=n;i++){
for(int j=0;j<10;j++){
if(pos[j].size()){
int dif=T.ask(n)-T.ask(pos[j].front());
int dist=pos[j].front()+dif-i;
if(dist<=k){
T.add(pos[j].front(),1);
pos[j].pop();
ans+=(char)(j+'0');
k-=dist;
break;
}
}
}
}
return ans;
}
};