“字典序”最小的01字符串

一、背景与问题描述

在算法与字符串处理的世界中,字典序最小问题是一类经典且有趣的挑战。在这个问题中,我们需要对由 01 组成的字符串进行最多 k 次操作,每次操作可以交换相邻的两个字符,目标是得到字典序最小的字符串。

字典序是字符串比较大小的一种方式,类似于词典中单词的排列顺序。例如,001 小于 010,因为 0 的优先级高于 1。在此问题中,我们需要通过有限次数的操作使得字符串尽可能靠近这种排列顺序。
在这里插入图片描述

例子分析

样例1:
输入字符串为 01010,最多可以进行 2 次相邻交换操作。
我们可以这样移动:

  • 第一次交换字符串变为 00110
  • 第二次交换字符串变为 00101
    最终,字典序最小的结果是 00101

样例2:
输入字符串为 1101001,最多可以进行 3 次相邻交换操作。
通过以下操作,我们可以得到结果:

  • 第一次交换,11010011011001
  • 第二次交换,10110010111001
  • 第三次交换,01110010110101
    最终结果为 0110101

二、分析与解决思路

问题建模

将问题分解,可以归纳出以下特性:

  1. 只需要关心 01 的相对顺序,目标是将 0 尽可能提前。
  2. 操作次数有限,因此不可能将所有的 0 都移到最前面,必须优先处理更靠后的 0
  3. 每次操作仅能交换相邻字符,因此无法直接跳跃,只能模拟每次交换的过程。
贪心策略

通过分析可以发现,贪心策略是本问题的最佳选择:

  • 遍历字符串,从左到右找到每一个 0
  • 如果 0 前面有 1,并且当前操作次数 k 足够,将这个 0 与前面的 1 交换,直到无法再交换或用尽操作次数;
  • 优先移动靠后的 0,因为前面的 0 已经在靠前的位置,对结果的影响较小。

三、Java 实现代码

public class Main {
    public static String solution(int n, int k, String s) {
        char[] chars = s.toCharArray(); // 转为字符数组
        int i = 0; // 遍历指针

        while (i < n && k > 0) {
            if (chars[i] == '0') {
                int pos = i;
                // 将当前的 '0' 尽可能往前移动
                while (pos > 0 && chars[pos - 1] == '1' && k > 0) {
                    chars[pos] = chars[pos - 1];
                    chars[pos - 1] = '0';
                    pos--; // 向左移动
                    k--;   // 消耗一次操作
                }
            }
            i++; // 移动到下一个字符
        }
        
        return new String(chars); // 转为字符串返回
    }

    public static void main(String[] args) {
        System.out.println(solution(5, 2, "01010").equals("00101")); // true
        System.out.println(solution(7, 3, "1101001").equals("0110101")); // true
        System.out.println(solution(4, 1, "1001").equals("0101")); // true
    }
}

四、算法复杂度分析

  1. 时间复杂度

    • 遍历字符串的复杂度为 (O(n));
    • 每次移动最多需要 (O(k)) 次操作,但总的移动次数受 k 的限制,因此整体复杂度为 (O(n))。
  2. 空间复杂度

    • 使用了字符数组存储字符串,额外空间为 (O(n))。
问题特性总结
  1. 局部最优即全局最优:
    因为操作是有限的,并且只允许相邻交换,因此每次只需关心当前的最优决策,而无需考虑全局调整。

  2. 启发式策略:
    本问题中的启发式是尽量将 0 提前,并优先移动靠后的 0,从而逐步优化整个字符串的字典序。


博客主页: 总是学不会.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值