刷题地址:144. 字典序最小的 01 字符串
题目:字典序最小的01字符串
题目描述:
小红有一个 01 字符串,她可以进行最多 k 次提作,每次操作可以交换相邻的两个字符,问可以得到的字典序最小的字符串是什么。
输入描述:
第一行包含两个整数,n(1 < n < 10^5)和 k(1 < k < 10^9),表示字符串的长度和可以进行的操作次数。
接下来一行一个长度为 n 的 01 字符串。
输出描述:
输出一个长度为 n 的字符串,表示字典序最小的字符串。
输入示例:
5 2 01010
输出示例:
00101
解题思路 :
采用双指针,用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的值
(1)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
(2)end=1,start不动标记第一个1的位置,end继续遍历
AC代码:
import java.util.*;
class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
String s = sc.next();
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++;
}
}
for (int i = 0; i < n; i++) {
System.out.print(sa[i]);
}
}
}