青训营-最小替换子串长度

5 阅读2分钟

问题描述

小F得到了一个特殊的字符串,这个字符串只包含字符ASDF,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得ASDF这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。


测试样例

样例1:

输入:input = "ADDF"
输出:1

样例2:

输入:input = "ASAFASAFADDD"
输出:3

样例3:

输入:input = "SSDDFFFFAAAS"
输出:1

样例4:

输入:input = "AAAASSSSDDDDFFFF"
输出:0

样例5:

输入:input = "AAAADDDDAAAASSSS"
输出:4

为了实现这个功能,可以使用滑动窗口和计数的方法,核心思路如下:

  1. 频次要求:字符串长度为4的倍数,假设其长度为 n,那么每个字符 ASDF 的理想频次应为 n / 4
  2. 滑动窗口:我们可以使用滑动窗口来检查一个子串,并尝试使得剩余字符的频次满足要求。我们在窗口中统计字符的出现频次,判断窗口外的字符是否满足平衡条件。
  3. 调整窗口:不断调整窗口的大小和位置,直到窗口内的字符数量可以满足平衡条件。这样可以获得最小的子串长度。

下面是完整的代码实现:

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static int solution(String input) {
        int n = input.length();
        int targetCount = n / 4;
        Map<Character, Integer> countMap = new HashMap<>();

        // 初始化每个字符的频次
        for (char c : input.toCharArray()) {
            countMap.put(c, countMap.getOrDefault(c, 0) + 1);
        }

        // 检查当前是否已经平衡
        if (isBalanced(countMap, targetCount)) return 0;

        int minLength = n;
        int left = 0;

        // 滑动窗口遍历
        for (int right = 0; right < n; right++) {
            // 窗口右边字符计数减少
            char rightChar = input.charAt(right);
            countMap.put(rightChar, countMap.get(rightChar) - 1);

            // 尝试平衡检查
            while (isBalanced(countMap, targetCount) && left <= right) {
                minLength = Math.min(minLength, right - left + 1);
                
                // 窗口左边字符计数恢复
                char leftChar = input.charAt(left);
                countMap.put(leftChar, countMap.get(leftChar) + 1);
                left++;
            }
        }

        return minLength;
    }

    // 判断字符频次是否满足平衡
    private static boolean isBalanced(Map<Character, Integer> countMap, int targetCount) {
        return countMap.getOrDefault('A', 0) <= targetCount &&
               countMap.getOrDefault('S', 0) <= targetCount &&
               countMap.getOrDefault('D', 0) <= targetCount &&
               countMap.getOrDefault('F', 0) <= targetCount;
    }

    public static void main(String[] args) {
        System.out.println(solution("ADDF") == 1);
        System.out.println(solution("ASAFASAFADDD") == 3);
        System.out.println(solution("SSDDFFFFAAAS") == 1);
        System.out.println(solution("AAAASSSSDDDDFFFF") == 0);
        System.out.println(solution("AAAADDDDAAAASSSS") == 4);
    }
}

代码说明:

  1. 计数初始化:首先统计输入字符串中每个字符的出现次数。
  2. 滑动窗口:使用左右指针(leftright)来定义窗口,调整窗口大小以寻找符合条件的最小子串。
  3. 判断是否平衡:每次移动窗口时检查窗口外的字符是否满足平衡要求。如果满足,更新最小长度。
  4. 输出结果:最终返回最小的子串长度。