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

124 阅读3分钟

问题理解

我们需要找到一个最小的子串长度,通过替换这个子串中的字符,使得整个字符串中字符 ASDF 的出现频次相等。由于字符串的长度总是4的倍数,因此每个字符的理想频次应该是 len(input) / 4

数据结构与算法步骤

  1. 计算理想频次:首先计算每个字符的理想频次,即 ideal_freq = len(input) / 4

  2. 统计当前频次:统计字符串中每个字符 ASDF 的当前频次。

  3. 计算需要替换的字符数:对于每个字符,计算它需要替换的次数,即 needed_replacements = max(0, current_freq - ideal_freq)

  4. 滑动窗口:使用滑动窗口技术来找到一个最小的子串,使得这个子串中包含所有需要替换的字符,并且替换次数满足要求。

代码如下

    public static int solution(String input) {
    int n = input.length();
    int targetFreq = n / 4; // 目标频次
    
        // 计算每个字符的初始频次
        int[] freq = new int[4]; // 0: 'A', 1: 'S', 2: 'D', 3: 'F'
        for (char c : input.toCharArray()) {
            freq[charToIndex(c)]++;
        }
    
        // 如果已经满足条件,直接返回0
        if (freq[0] == targetFreq && freq[1] == targetFreq && freq[2] == targetFreq && freq[3] == targetFreq) {
            return 0;
        }
    
        // 滑动窗口
        int left = 0, right = 0;
        int minLen = n; // 初始化最小长度为字符串长度
        int[] windowFreq = new int[4]; // 窗口内字符频次
    
        while (right < n) {
            // 更新窗口内字符频次
            windowFreq[charToIndex(input.charAt(right))]++;
            right++;
    
            // 检查是否可以通过替换窗口内的字符达到目标频次
            while (canBalance(freq, windowFreq, targetFreq)) {
                minLen = Math.min(minLen, right - left);
                // 缩小窗口
                windowFreq[charToIndex(input.charAt(left))]--;
                left++;
            }
        }
    
        return minLen;
    }
    
    // 将字符转换为索引
    private static int charToIndex(char c) {
        switch (c) {
            case 'A': return 0;
            case 'S': return 1;
            case 'D': return 2;
            case 'F': return 3;
            default: throw new IllegalArgumentException("Invalid character: " + c);
        }
    }
    
    // 检查是否可以通过替换窗口内的字符达到目标频次
    private static boolean canBalance(int[] freq, int[] windowFreq, int targetFreq) {
        for (int i = 0; i < 4; i++) {
            if (freq[i] - windowFreq[i] > targetFreq) {
                return false;
            }
        }
        return true;
    }
    
    public static void main(String[] args) {
        // 你可以添加更多测试用例
        System.out.println(solution("ADDF") == 1);
        System.out.println(solution("ASAFASAFADDD") == 3);
    }
}

关键步骤解释:

  1. 滑动窗口初始化:我们使用两个指针 leftright 来表示当前窗口的边界。window_count 用于记录当前窗口内每个字符的出现次数。

  2. 滑动窗口逻辑:我们遍历字符串,每次将右指针 right 向右移动,并更新 window_count。然后检查当前窗口是否满足替换条件(即窗口内每个字符的出现次数都大于等于需要替换的次数)。如果满足条件,我们尝试缩小窗口(移动左指针 left),并更新最小子串长度。

  3. 返回结果:最终返回找到的最小子串长度。

学习收获: 1.优化时间复杂度: 通过滑动窗口和哈希表的结合使用,可以在O(n)的时间复杂度内解决问题,避免了使用暴力解法导致的O(n^2)或更高的时间复杂度。 2.提升算法设计能力: 解决此类问题需要对滑动窗口和哈希表有深入的理解,并能够灵活地将它们结合起来使用。这有助于提升算法设计能力和问题解决能力。 3.增强逻辑思维: 在滑动窗口的过程中,需要不断地判断窗口中的字符串是否包含t中的所有字符,这要求具备较强的逻辑思维能力。通过不断地练习和思考,可以进一步增强逻辑思维和问题解决能力。 4.拓展应用场景: 最小替换子串长度算法不仅适用于字符串处理领域,还可以拓展到其他领域,如数组处理、子序列问题等。通过掌握这种算法思想,可以更好地解决类似的问题。