问题描述
小U拥有一个由0和1组成的字符串,她可以进行最多k次操作,每次操作可以交换相邻的两个字符。目标是通过这些操作,使得最终得到的字符串字典序最小。
例如,小U当前有一个字符串 01010,她最多可以进行 2 次相邻字符交换操作。通过这些操作,她可以将字符串调整为 00101,这是可以通过不超过2次操作得到的字典序最小的字符串。
现在,小U想知道,经过最多k次操作后,能够得到的字典序最小的字符串是什么。
思路
- 该问题可以通过使用两个指针来进行同步滑动来判定对应的情况
- 一共有四种情况包括
01:该情况不需要交换位置00:该情况也不需要交换位置10:该情况需要交换位置11:该情况也不需要交换位置,但需要判定下一个0在哪
- 所以考虑left指针指向第一个1,让right直到碰到连续
1后的第一个0即:11110情况,从右向左依次交换 - 右向左遍历时需要一个临时指针来对right进行处理
代码实现
#include <iostream>
#include <vector>
#include <string>
using namespace std;
string solution(int n, int k, string s)
{
// write code here
int left = 0, right = 1, time = 0; // 初始化两个指针,time表示交换次数
while (right <= n && time < k) // 判定条件次数不超过且右指针不出界
{
if (s[left] == '1') // 进入内部遍历
{
while (s[right] != '0' && right <= n)
++right; // 找到第一个不是0的索引
int tmp = right - 1;
while (time < k && tmp >= left && tmp < n - 1)
{
swap(s[tmp], s[right]); // 遍历进行交换
tmp--;
right--;
time++;
}
}
else
{
left++; // 为0则同时右移
right++;
}
}
// cout << s << endl;
return s;
}
int main() {
cout << (solution(5, 2, "01010") == "00101") << endl;
cout << (solution(7, 3, "1101001") == "0110101") << endl;
cout << (solution(4, 1, "1001") == "0101") << endl;
return 0;
}
- 最需要关注的是测试时第四个样例:
cout << (solution(5, 12, "00101") == "00011") << endl; - 一开始编写的代码过不了该样例,经过debug后发现进入死循环,发现right指针一直未越过原始字符串所以通过将第二层遍历while将right超过n来最终跳出循环返回
算法分析
- 外层循环最多遍历整个字符串执行n次,即O(n)
- 内层循环找right,最坏情况,若没有0则会从当前值移动到末尾,执行O(n)次
- 内层循环交换,受交换次数限制最多为k次,即O(k)
总复杂度
时间复杂度:最坏情况下复杂度为O(n+k) 空间复杂度:O(1)