1、题目描述
解题思路 :
采用双指针,用start标记最前面的1的位置,用end标记最后面的1的下一位(即start到end是连续的1子串),尽量往后换,可能11后面有0,当找到0时,不断往前交换,直到k=0 110,
开始的思路:需要换的场景只有10,01不用换,11没法换,但11后面可能有0,当找到0时,不断往前交换,尽量让0和第一个1交互,这样才能保证字典序最小,重复这个过程,直到k=0
思路优化:不用依次交换,如果有一组连着的1,例如111111,并且也不用交互,一共01两个值,直接赋值就可以。
大体流程:
遍历字符串,直到end到末尾或者k=0
start和end初始为0
每次循环判断end的值
一、end=0;
这时候有两种情况:
1、当前位置前面全是0,不需要交互,start,end继续往后遍历;
2、当前位置前面有1,需要交换
(1)如果k>end-start,则直接交换开头的1, start++,end继续找下一个为0的位置
(2)如果k=0,则不交换,需要退出循环了
(3)如果0<k<end-start, 末尾end左移k位,交换end+1位和end-k位,k=0
二、end=1,start不动标记第一个1的位置,end继续遍历
代码如下:
public class Main {
public static String solution(int n1, int k1, String str) {
// write code here
int n = n1;
int k = k1;
String s = str;
char[] sa = s.toCharArray();
int start = 0, end = 0;
// 遍历end直到末尾,同时保证k>0
while (end < n && k > 0) {
// 如果当前位置是0,需要判断当前位置前面有没有1,
if (sa[end] == '0') {
// 这表示还没开始交换过,前面没有1,前边都是0
if (start == end) {
start++;
end++;
} else { // 前边有1,需要判断能否交换
if (k >= end - start) {
sa[start] = '0';
sa[end] = '1';
start++;
end++;
k -= end - start;
} else { // k=0的情况不存在,每次遍历都过滤掉了,只有k>0
sa[end - k] = '0';
sa[end] = '1';
k = 0;
}
}
} else { // 如果是1,start就不动了,end向后遍历
end++;
}
}
String res = new String(sa);
return res;
}
public static void main(String[] args) {
System.out.println(solution(5, 2, "01010").equals("00101"));
System.out.println(solution(7, 3, "1101001").equals("0110101"));
System.out.println(solution(4, 1, "1001").equals("0101"));
}
}
代码解析
- 字符串转换为字符数组:
- 因为字符串是不可变的,在需要修改字符串内容时,首先将字符串转化为字符数组
arr。这样可以在字符数组上直接进行操作。
- 遍历字符串:
- 从字符串的左侧开始遍历。定义双指针start,end,如果当前字符是
'0',则尝试将其向前交换。
- 交换操作:
- 如果遇到一个
'0',从其左边开始扫描,找到最近的'1'并进行交换。每交换一次,k -= end - start;。
- 输出结果:
- 经过遍历和交换操作后,得到最终的字符数组,然后将其转回字符串并返回。
总结
双指针算法的代码结构通常比较清晰,通过两个指针的移动来处理数据,很容易理解算法的思路。双指针可以跳过已经处理过的元素或者不符合条件的元素组合。