最小替换子串长度 | 豆包MarsCode AI刷题

41 阅读5分钟

一、题目分析

本题要求在一个只包含字符 A、S、D、F 且长度为 4 的倍数的字符串中,通过尽可能少的替换操作,使这四个字符出现的频次相等,并求出满足此条件的最小子串长度。这本质上是一个关于字符串处理和滑动窗口算法应用的问题。

二、思路阐述

上述提供的 Java 代码是解决这个问题的一种有效方法。这个问题的核心在于找到一个子串,通过尽可能少的替换操作,使得字符串中特定的四个字符(A、S、D、F)出现的频次相等。

首先,理解问题的关键在于认识到字符串的长度总是 4 的倍数这一条件的重要性。这意味着理论上可以将字符串平均分成四份,使得每个字符在最终的理想状态下出现的次数相同。

代码中的solution方法是解决问题的核心部分。它首先计算出每个字符的理想频次,即字符串总长度除以 4。这个理想频次将作为判断子串是否满足条件的重要依据。

接着,通过遍历整个字符串,分别统计出 A、S、D、F 这四个字符在整个字符串中出现的次数。这个步骤为后续的滑动窗口操作提供了基础数据。

滑动窗口是解决这个问题的关键算法。从字符串的开头开始,逐渐扩大窗口(右指针右移),同时更新窗口内每个字符的出现次数。这个过程就像是在一个不断变大的窗口中观察字符的分布情况。

一旦窗口内的字符分布满足一定条件,即通过isEqual方法判断四个字符的频次有可能在经过一定的替换操作后达到相等,就开始尝试缩小窗口(左指针右移)。这个过程是为了找到最小的满足条件的子串长度。在缩小窗口的过程中,需要不断更新窗口内字符的出现次数,并重新判断是否仍然满足条件。

isEqual方法在整个算法中起到了关键的判断作用。它通过比较窗口内每个字符的出现次数与整个字符串中该字符的出现次数,以及与理想频次的关系,来确定当前窗口是否有可能成为满足条件的子串。

这种算法的优势在于它能够在不需要遍历所有可能的子串的情况下,高效地找到最小替换子串长度。通过巧妙地利用滑动窗口和适当的判断条件,可以大大减少计算量,提高算法的效率。

在实际应用中,这种类型的问题可能会出现在文本处理、数据分析等领域。例如,在处理大量的文本数据时,需要找到特定字符或模式的最佳分布情况,以满足某种特定的要求。

总之,通过对 “最小替换子串长度” 问题的分析和解决,我们不仅可以提高自己的编程能力,还能深入理解算法设计的原理和应用。上述 Java 代码为我们提供了一个有效的解决方案,同时也为我们在面对类似问题时提供了一种思考方式和解决方法。

三、代码实现

import java.util.HashMap;

public class Main {
    public static int solution(String input) {
        int length = input.length();
        // 计算每个字符的理想频次
        int targetCount = length / 4;

        // 记录整个字符串中每个字符的出现次数
        int countA = 0, countS = 0, countD = 0, countF = 0;
        for (char c : input.toCharArray()) {
            if (c == 'A') {
                countA++;
            } else if (c == 'S') {
                countS++;
            } else if (c == 'S') {
                countD++;
            } else if (c == 'F') {
                countF++;
            }
        }

        // 用哈希表记录窗口内每个字符的出现次数
        HashMap<Character, Integer> windowMap = new HashMap<>();
        int left = 0;
        int minLength = length;

        for (int right = 0; right < length; right++) {
            // 更新窗口内字符的出现次数
            char rightChar = input.charAt(right);
            windowMap.put(rightChar, windowMap.getOrDefault(rightChar, 0) + 1);

            // 检查是否满足四个字符频次相等的条件
            while (isEqual(windowMap, countA, countS, countD, countF, targetCount)) {
                // 更新最小子串长度
                minLength = Math.min(minLength, right - left + 1);

                // 缩小窗口,更新窗口内字符的出现次数
                char leftChar = input.charAt(left);
                windowMap.put(leftChar, windowMap.get(leftChar) - 1);
                if (windowMap.get(leftChar) == 0) {
                    windowMap.remove(leftChar);
                }
                left++;
            }
        }

        return minLength;
    }

    private static boolean isEqual(HashMap<Character, Integer> windowMap, int countA, int countS, int countD, int countF, int targetCount) {
        int windowCountA = windowMap.getOrDefault('A', 0);
        int windowCountS = windowMap.getOrDefault('S', 0);
        int windowCountD = windowMap.getOrDefault('D', 0);
        int windowCountF = windowMap.getOrDefault('F', 0);

        return windowCountA + countA >= targetCount &&
                windowCountS + countS >= targetCount &&
                windowCountD + countD >= targetCount &&
                windowCountF + countF >= targetCount &&
                windowCountA <= targetCount &&
                windowCountS <= targetCount &&
                windowCountD <= targetCount &&
                windowCountF <= 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);
    }
}

在上述代码中:

  • solution方法实现了核心的算法逻辑,通过滑动窗口在字符串上移动,不断调整窗口大小以找到满足四个字符出现频次相等条件的最小子串长度。
  • isEqual方法用于判断当前窗口内的字符频次情况是否符合最终四个字符频次相等的要求。
  • main方法中,对题目给出的部分测试样例以及额外添加的一些测试样例进行了验证,通过比较solution方法的返回值与预期值是否相等来判断代码的正确性。