字典里面的最小01字符 | 豆包MarsCode AI刷题

90 阅读2分钟

题干描述:

image.png

解题思路:

这道题乍一看不清楚有什么思路,其实多思考一会就能想通,最主要是在开始可能会进入思维误区,将1尽量换到最后面的零的位置去,这其实也符合贪心的策略,但是确并不符合局部最优,因为你把1换到后面但是如果换的路径上还有1,这时候一开始换的1就不一定是最贪心的那个了。因此,再次思考之后,反过来想,把零提到最前面的1的这个位置不就好了。(也就是说,将1换到最近的零的位置,遍历字符串,把每一个前面的一都换到最近的零的位置)

代码解析:

首先先将字符串转换成字符串数组,方便交换它们的位置,接着按照我们的思路,找到第一个1之后的第一个0,通过一个循环遍历即可找到。然后就是计算交换的步数,就是0的位置减去当前1的位置(记得还需要减去一)。

然后就进入了我们的重点,这里我们需要从后往前交换,因为如果从前往后交换的话就会产生多余的交换(比如:1101001,当i遍历到第一个1时候,交换之后会把第二个1给交换到第一位去,这样就不符合我们的局部最优的策略,并没有交换之后最小)所以我们应该要采取将0提到前面的这种交换顺序,使得交换过后能使局部达到最优解。

最后也别忘记更新我们的k值,保证下次循环顺利。

public static String solution(int n, int k, String s) {
        char[] chars = s.toCharArray();
        //明确贪心的策略在于将一移动到尽量后面,也就是将零移动到尽量前面
        for (int i = 0; i < n && k > 0; i++) {
            // 从当前位置开始,找到最近的零
            int fistZero = i;
            for (int j = i + 1; j < n; j++) {
                if (chars[j] == '0' && chars[i] == '1') {
                    fistZero = j;break;
                }
            }
            
            // 计算将最小字符移动到当前位置所需的交换次数
            int swapsNeeded = fistZero - i;
            
            // 如果交换次数不超过 k,则进行交换
            if (swapsNeeded <= k) {
                // 将最小字符移动到当前位置,不断的左右交换
                for(int j = fistZero; j >i; j--){
                    char temp = chars[j];
                    chars[j] = chars[j - 1];
                    chars[j - 1] = temp;
                }
                // 更新 k
                k -= swapsNeeded;
            }
        }
        
        return new String(chars);
    }

总结

本题虽然在难度上被标出难题,但是实际上如果思考清楚这个局部最优解的问题,捋清楚交换顺序以及交换的位置,其实是一攻就破的,果然,贪心算法都是常识突破题目。