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

66 阅读3分钟

最小替换子串长度问题

问题描述

在一个包含字符 A, S, D, F 的字符串中,使这四个字符的出现频率尽可能相等。目标是通过替换子串,使得字符串满足这一条件,并求出最小的子串长度。


解题思路

这道题的核心在于使用滑动窗口(Sliding Window)技术和哈希表来高效判断一个区间内字符的分布情况。

关键步骤

  1. 统计字符频率

    • 先统计每个字符在输入字符串中的频率,判断是否已经满足条件(所有字符的频率 ≤ 总长度 / 4)。
    • 如果满足条件,直接返回 0
  2. 滑动窗口

    • 使用两个指针 leftright 表示滑动窗口的起始和结束位置。
    • 每次移动右指针,将字符从窗口中移除(对应的频率减一)。
    • 当窗口中剩余字符的频率满足条件时,尝试收缩窗口(移动左指针)。
  3. 动态更新最小子串长度

    • 如果窗口内剩余字符满足条件,则记录当前窗口的长度,并尝试继续收缩以获得更小的窗口。

代码实现

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

public class Main {
    public static int solution(String input) {
        int n = input.length();
        int target = n / 4;  // 每个字符的目标频率
        Map<Character, Integer> count = new HashMap<>();
        
        // 初始化字符频率
        for (char c : input.toCharArray()) {
            count.put(c, count.getOrDefault(c, 0) + 1);
        }
        
        // 检查是否已经符合要求
        if (isBalanced(count, target)) return 0;
        
        int left = 0, minLength = n;
        
        // 滑动窗口遍历
        for (int right = 0; right < n; right++) {
            // 移除右边字符
            count.put(input.charAt(right), count.get(input.charAt(right)) - 1);
            
            // 如果满足条件,尝试收缩窗口
            while (isBalanced(count, target) && left <= right) {
                minLength = Math.min(minLength, right - left + 1);
                count.put(input.charAt(left), count.get(input.charAt(left)) + 1);
                left++;
            }
        }
        
        return minLength;
    }
    
    // 检查当前频率是否满足要求
    private static boolean isBalanced(Map<Character, Integer> count, int target) {
        return count.getOrDefault('A', 0) <= target &&
               count.getOrDefault('S', 0) <= target &&
               count.getOrDefault('D', 0) <= target &&
               count.getOrDefault('F', 0) <= target;
    }

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

代码详解

  1. 初始化频率统计

    • 使用 HashMap 存储字符频率,键为字符,值为字符出现的次数。
    • 使用 getOrDefault 方法处理不存在的字符。
  2. 滑动窗口操作

    • right 指针扩展窗口,将字符从统计中移除(对应值减一)。
    • 判断当前窗口内剩余字符的分布是否满足目标条件。
    • 如果满足,开始收缩窗口,尝试更新最小长度。
  3. 判断频率分布是否符合要求

    • 定义一个辅助方法 isBalanced,检查 A, S, D, F 是否均小于等于 target

测试用例分析

  • 用例1: 输入:ADDF
    过程:字符频率为 A=1, D=2, F=1,目标值为 4 / 4 = 1,替换一个 D 即可。
    输出:1
  • 用例2: 输入:ASAFASAFADDD
    过程:字符频率初始为 A=5, S=4, F=2, D=1,通过滑动窗口找到子串 ASAF,替换 3 个字符即可。
    输出:3
  • 用例3: 输入:SSDFFFASADDDFFF
    过程:字符频率为 S=4, D=4, F=5, A=1,只需替换一个字符即可满足条件。
    输出:1
  • 用例4: 输入:AAAASSSSDDDDFFFF
    过程:频率为 A=4, S=4, D=4, F=4,已经满足条件,无需替换。
    输出:0

扩展与思考

  1. 优化点

    • 当前解法时间复杂度为 O(n),适合处理大规模数据。
    • 频率统计使用 HashMap,如果字符集合固定,可直接使用数组提高访问效率。
  2. 适用场景

    • 适用于任何需要调整子数组以满足条件的场景。
    • 滑动窗口与哈希表的结合是处理连续性问题的经典模式。
  3. 类比问题

    • 找到具有指定和的最短子数组。
    • 求解包含所有指定字符的最短子字符串。

通过这个问题,我们可以更加深入理解滑动窗口技术的核心逻辑,特别是如何动态调整窗口大小和统计频率分布。这种思想在数据流处理和字符串操作中具有广泛应用。