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

2 阅读6分钟

问题描述

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


测试样例

样例1:

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

样例2:

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

样例3:

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

样例4:

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

样例5:

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

使用 MarsCode AI 来解题


我:需要一点思路提示

MarsCode AI:

好的,让我们来分析一下这个问题。

问题理解

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

数据结构选择

  1. 计数器:我们可以使用一个计数器来统计每个字符 ASDF 的出现次数。
  2. 滑动窗口:为了找到最小的替换子串,我们可以使用滑动窗口技术来遍历字符串,并计算每个窗口内需要替换的字符数。

算法步骤

  1. 初始化计数器:统计字符串中每个字符的出现次数。
  2. 计算理想频次:计算每个字符的理想频次 ideal_count = len(input) / 4
  3. 计算需要替换的字符数:对于每个字符,计算需要替换的字符数 needed_replacements = max(0, count - ideal_count)
  4. 滑动窗口:使用滑动窗口技术,从左到右遍历字符串,计算每个窗口内需要替换的字符数,并记录最小的窗口长度。

伪代码

public class Main {
    public static int solution(String input) {
        // 初始化计数器
        int[] count = new int[4];
        for (char c : input.toCharArray()) {
            count[charToIndex(c)]++;
        }

        // 计算理想频次
        int ideal_count = input.length() / 4;

        // 计算需要替换的字符数
        int needed_replacements = 0;
        for (int i = 0; i < 4; i++) {
            if (count[i] > ideal_count) {
                needed_replacements += count[i] - ideal_count;
            }
        }

        // 滑动窗口
        int left = 0, right = 0;
        int[] window_count = new int[4];
        int min_length = input.length();

        while (right < input.length()) {
            // 扩展窗口
            window_count[charToIndex(input.charAt(right))]++;
            right++;

            // 收缩窗口
            while (canReplace(window_count, count, ideal_count)) {
                min_length = Math.min(min_length, right - left);
                window_count[charToIndex(input.charAt(left))]--;
                left++;
            }
        }

        return min_length;
    }

    // 辅助函数:将字符转换为索引
    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");
        }
    }

    // 辅助函数:判断当前窗口是否可以替换
    private static boolean canReplace(int[] window_count, int[] count, int ideal_count) {
        for (int i = 0; i < 4; i++) {
            if (window_count[i] < count[i] - ideal_count) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        // 你可以添加更多测试用例
        System.out.println(solution("ADDF") == 1);
        System.out.println(solution("ASAFASAFADDD") == 3);
    }
}

我:没理解这个输出要的是什么

MarsCode AI:

好的,让我们详细解释一下这个问题的输出要求。

问题描述回顾

小F的任务是通过尽可能少的替换,使得字符串中字符 ASDF 的出现频次相等。我们需要找到一个最小的子串长度,通过替换这个子串中的字符,使得整个字符串中这四个字符的频次相等。

输出解释

输出是一个整数,表示实现这一条件的最小子串长度。具体来说:

  1. 理想频次:由于字符串的长度总是4的倍数,每个字符的理想频次应该是 len(input) / 4
  2. 需要替换的字符数:对于每个字符,计算需要替换的字符数 needed_replacements = max(0, count - ideal_count)
  3. 最小子串长度:我们需要找到一个最小的子串,通过替换这个子串中的字符,使得整个字符串中字符 ASDF 的频次相等。

示例解释

让我们通过几个示例来进一步理解:

样例1
输入:"ADDF"
输出:1 理想频次:4 / 4 = 1
当前频次:A: 1, S: 0, D: 2, F: 1
需要替换的字符数:A: 0, S: 1, D: 1, F: 0
最小子串长度:只需要替换一个字符(例如将一个 D 替换为 S),所以输出为 1

样例2
输入:"ASAFASAFADDD"
输出:3
理想频次:12 / 4 = 3
当前频次:A: 5, S: 3, D: 3, F: 1
需要替换的字符数:A: 2, S: 0, D: 0, F: 2
最小子串长度:需要替换三个字符(例如将两个 A 替换为 F,一个 F 替换为 A),所以输出为 3


看完以后,我的理解是这样的,先统计所有的字符个数,看看哪个字符多了几个,比如A多了2个,那么就会有其他字符的和少了2个,现在找出字符数量更多的,假如A多了2个,D多了1一个,那么通过滑动窗口找最小窗口,窗口内的字符串必须至少包含2个A和一个D,同时保证窗口最小。

但是运行代码报错如下:

[WARN]
[WARN] 解答错误,请修正代码后重新提交
[WARN]4 个测试用例运行错误,3/35 个用例测试通过
[WARN]
[WARN] 测试用例输入:
[WARN]    input = "AAAASSSSDDDDFFFF"
[ERROR]
[ERROR] java.lang.StringIndexOutOfBoundsException: String index out of range: 16
	at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
	at java.base/java.lang.String.charAt(String.java:1519)
	at Main.solution(Main.java:34)
	at TestRunnerMain.lambda$runCase$0(TestRunnerMain.java:56)
	at java.base/java.lang.Thread.run(Thread.java:842)

把错误给MarsCode AI,并说说有没有哪些情况没有考虑到


MarsCode AI:

可能遗漏的情况

  1. 边界情况:字符串长度为4的倍数,但所有字符的频次已经相等。
  2. 滑动窗口的初始化:确保滑动窗口的初始状态正确。
  3. 多余字符的计算:确保多余字符的计算正确。

检查边界情况

  1. 所有字符频次相等:如果所有字符的频次已经相等,那么不需要进行任何替换,直接返回 0

修改代码

public class Main {
    public static int solution(String input) {
        // 初始化计数器
        int[] count = new int[4];
        for (char c : input.toCharArray()) {
            count[charToIndex(c)]++;
        }

        // 计算理想频次
        int ideal_count = input.length() / 4;

        // 检查是否所有字符频次已经相等
        boolean allEqual = true;
        for (int i = 0; i < 4; i++) {
            if (count[i] != ideal_count) {
                allEqual = false;
                break;
            }
        }
        if (allEqual) {
            return 0;
        }

        // 计算多余字符数
        int[] excess = new int[4];
        for (int i = 0; i < 4; i++) {
            excess[i] = Math.max(0, count[i] - ideal_count);
        }

        // 滑动窗口
        int left = 0, right = 0;
        int[] window_count = new int[4];
        int min_length = input.length();

        while (right < input.length()) {
            // 扩展窗口
            window_count[charToIndex(input.charAt(right))]++;
            right++;

            // 收缩窗口
            while (containsExcess(window_count, excess)) {
                min_length = Math.min(min_length, right - left);
                window_count[charToIndex(input.charAt(left))]--;
                left++;
            }
        }

        return min_length;
    }

    // 辅助函数:将字符转换为索引
    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");
        }
    }

    // 辅助函数:判断当前窗口是否包含所有多余字符
    private static boolean containsExcess(int[] window_count, int[] excess) {
        for (int i = 0; i < 4; i++) {
            if (window_count[i] < excess[i]) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        // 你可以添加更多测试用例
        System.out.println(solution("ADDF") == 1);
        System.out.println(solution("ASAFASAFADDD") == 3);
    }
}

通过