邻位交换的最小次数
给你一个表示大整数的字符串 num ,和一个整数 k 。
如果某个整数是 num 中各位数字的一个 排列 且它的 值大于 num ,则称这个整数为 妙数 。可能存在很多妙数,但是只需要关注 值最小 的那些。
例如,num = “5489355142” :
第 1 个最小妙数是 “5489355214”
第 2 个最小妙数是 “5489355241”
第 3 个最小妙数是 “5489355412”
第 4 个最小妙数是 “5489355421”
返回要得到第 k 个 最小妙数 需要对 num 执行的 相邻位数字交换的最小次数 。
测试用例是按存在第 k 个最小妙数而生成的。
示例 1:
输入:num = “5489355142”, k = 4
输出:2
解释:第 4 个最小妙数是 “5489355421” ,要想得到这个数字:
- 交换下标 7 和下标 8 对应的位:“5489355142” -> “5489355412”
- 交换下标 8 和下标 9 对应的位:“5489355412” -> “5489355421”
示例 2:
输入:num = “11112”, k = 4
输出:4
解释:第 4 个最小妙数是 “21111” ,要想得到这个数字:
- 交换下标 3 和下标 4 对应的位:“11112” -> “11121”
- 交换下标 2 和下标 3 对应的位:“11121” -> “11211”
- 交换下标 1 和下标 2 对应的位:“11211” -> “12111”
- 交换下标 0 和下标 1 对应的位:“12111” -> “21111”
示例 3:
输入:num = “00123”, k = 1
输出:1
解释:第 1 个最小妙数是 “00132” ,要想得到这个数字:
- 交换下标 3 和下标 4 对应的位:“00123” -> “00132”
提示:
- 2 <= num.length <= 1000
- 1 <= k <= 1000
- num 仅由数字组成
思路:
1、首先找到第k个妙数,求k次“下一个排列”得到第k个妙数。
2、遍历原字符串num和第k个妙数的字符串ans,以ans为参考,如果对应索引 i 处字符相同,跳过;否则,从 i 位置向后遍历num,找到第一个与ans[ i ]相同的字符,然后再向前一个一个交换。结束之后 i++
class Solution {
public:
int getMinSwaps(string num, int k) {
int n = num.size();
string ans = "";
//复制一份原字符串
for(int i=0; i<num.size(); i++){
ans.push_back(num[i]);
}
//得到第k次变换后的字符串
for(int i=0; i<k; i++){
ans = nextPermutation(ans);
}
int cnt = 0;
for(int i=0; i<n; i++){
//如果不同,进行处理
if(num[i] != ans[i]){
int j = i + 1;
//向后找到第一个与ans[i]相同的字符
while(num[j] != ans[i]){
j++;
}
//逐个向前交换,直到nums[j]换到i的位置
for(; j>i; j--){
swap(num, j, j - 1);
cnt++;
}
}
}
return cnt;
}
string nextPermutation(string& nums) {
int n = nums.size();
int i = n - 1;
//从后向前找到第一段升序序列,i位置是个峰值,i之后的序列降序
while(i > 0 && nums[i - 1] >= nums[i]){
i--;
}
//如果i == 0,说明整个数组是降序的,最后直接翻转为升序
//否则
if(i != 0){
int j = n - 1;
//因为i之后降序,所以找到的第一个j的位置,即nums[j] > nums[i - 1],一定是尽量小的那个较大数
while(j >= i && nums[i - 1] >= nums[j]){
j--;
}
//交换较小数和较大数
swap(nums, i - 1, j);
}
//较大数之后的部分升序排列
reverse(nums.begin() + i, nums.end());
return nums;
}
void swap(string& nums, int i, int j){
char temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
};