字典序最小的01字符串
题目解析
本题的目标是通过最多 k 次相邻字符交换操作,使一个由 0 和 1 组成的字符串的字典序最小。字典序最小意味着 0 要尽可能地排在前面。
思路分析
- 核心问题是找到并尽可能将字符串中的 0 向前交换,以达到字典序最小的效果。操作限制是每次只能交换相邻的两个字符,且最多可以进行
k次操作。 - 关键点:字符串中 0 的位置需要尽量向前移动,而不违背交换次数的限制。最简单的方法是,找到第一个 1 后面最近的 0,然后交换它们,直到无法继续交换或达到最大操作次数。
代码详解
-
初始化:
- 将输入的字符串转化为字符数组
arr,方便在原数组上进行操作。 - 使用数组
pos0存储所有字符 '0' 的位置。 - 使用
cnt统计字符串中 0 的个数,方便后续定位。
- 将输入的字符串转化为字符数组
-
找到第一个 '1' :
- 使用
last标记字符串中第一个 '1' 的位置。
- 使用
-
交换操作:
- 当
last小于当前位置的 '0' 时,尝试交换,直到交换次数k用完。 - 如果当前要交换的 '0' 可以通过交换与 '1' 交换达到字典序更小的效果,就交换它们。
- 当
-
处理剩余的 0 和 1:
- 更新交换后的字符串,并继续寻找可以进行交换的 '0',直到无法继续交换为止。
-
最终返回结果:
- 完成交换后,返回更新后的字符串作为答案。
代码实现
public class Main {
public static String solution(int n, int k, String s) {
char[] arr = s.toCharArray();
int[] pos0 = new int[n + 1];
int cnt = 0;
int last = 0;
for (int i = 0; i < n; i++) {
if (arr[i] == '0') {
pos0[++cnt] = i + 1;
} else if (last == 0) {
last = i + 1;
}
}
if (last == 0 || last >= pos0[cnt]) {
return s;
}
int i = 1;
while (i <= cnt && last > pos0[i]) {
i++;
}
while (k > 0 && i <= cnt && last <= n) {
int R0 = pos0[i];
int L1 = last;
if (R0 - L1 <= k) {
k -= (R0 - L1);
arr[R0 - 1] = '1';
arr[L1 - 1] = '0';
last++;
i++;
} else {
arr[R0 - 1] = '1';
arr[R0 - 1 - k] = '0';
k = 0;
}
while (last <= n && arr[last - 1] == '0') {
last++;
}
}
return new String(arr);
}
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"));
}
}
知识总结
在使用豆包MarsCode AI刷题过程中,我总结出了一些有用的知识点:
- 字符串操作:对于这类字符串交换的问题,可以通过将字符串转换为字符数组进行操作,这样更方便操作和修改字符。
- 贪心算法:在本题中,我们采用贪心算法的思想,尽可能将 '0' 向前移动,这样能最大程度地降低字典序。
- 边界条件:在处理字符串时,必须考虑到所有的边界情况,如没有 '1' 的情况、所有 '1' 都在 '0' 后面等,这些都需要在算法中进行合理判断和处理。
- 优化交换次数:利用
k的限制,尽可能在最小交换次数内实现目标。通过对位置的合理判断,避免不必要的交换。
学习计划
通过在豆包MarsCode AI平台刷题,我总结出了一个高效学习算法的计划:
- 制定学习目标:每周设定一个学习目标,比如掌握常见的算法和数据结构,尤其是排序、动态规划和图论等。
- 错题本:对于每次刷题时出现的错误,可以记录下来并总结原因,定期复习这些错题,确保理解每一个错误的背后逻辑。
- 重点难点突破:每次遇到难题时,分析题目的思路和代码,理解其中的细节,特别是那些优化方法和空间复杂度控制。