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

9 阅读5分钟

最小替换子串长度

问题描述

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

对题目的理解:

字符串的组成

  • 字符串只包含 ASDF 这四个字符。
  • 字符串的长度总是4的倍数。

目标

  • 通过尽可能少的替换操作,使得字符串中 ASDF 这四个字符的出现频次相等。
  • 替换操作是指将字符串中的某个字符替换为另一个字符。通过替换其中的字符,使得整个字符串中 ASDF 的频次相等。
  • 替换操作是原地进行元素的更改,不需要后面的数组含有相同的元素(这种是交换)

测试样例

样例1:

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

样例2:

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

样例3:

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

示例解释

样例1:

输入:"ADDF"

输出:1

  • 解释:字符串 "ADDF" 中,A 出现1次,S 出现0次,D 出现2次,F 出现1次。
  • 可以通过替换 D 为 S,得到 "ADSF",此时每个字符出现1次,频次相等。
  • 替换的最小子串长度为1。

样例2:

输入:"ASAFASAFADDD"

输出:3

  • 解释:字符串 "ASAFASAFADDD" 中,A 出现4次,S 出现3次,D 出现3次,F 出现2次。
  • 可以通过替换 "DDD" 为 "FFF",得到 "ASAFASAFFFF",此时每个字符出现4次,频次相等。
  • 替换的最小子串长度为3。

样例3:

输入:"SSDDFFFFAAAS"

输出:1

  • 解释:字符串 "SSDDFFFFAAAS" 中,A 出现3次,S 出现3次,D 出现2次,F 出现4次。
  • 可以通过替换 F 为 D,得到 "SSDDDDDDAAAS",此时每个字符出现4次,频次相等。
  • 替换的最小子串长度为1。

通过对样例和题目的分析,我们可以选用滑动窗口这一方法解决这一题目。

解题思路:

变量初始化

  • len:存储字符串的长度。
  • n:目标字符出现次数,每个字符应出现 n = len / 4 次才能平衡。
  • s:将输入字符串 input 转换为字符数组,便于逐字符操作。
  • left:窗口的左边界,初始为 0。
  • ans:记录最小窗口长度,初始设为 len
  • cnt:数组用于统计每个字符的频率。

统计字符频率

遍历字符数组 s,统计每个字符的出现次数,结果存储在 cnt 数组中。

初步检查平衡状态

  • 如果字符 'A','S','D','F' 的频率均等于 n,即 cnt['A'] == n && cnt['S'] == n && cnt['D'] == n && cnt['F'] == n,则字符串已经平衡,无需替换,直接返回 0。

滑动窗口寻找最小子串

  • 遍历字符串的右边界 right

    • 每次将右边界的字符 s[right] 计数减 1,表示将该字符移入窗口。
    • 然后检查窗口内部的字符分布:只要 cnt['A'] <= n && cnt['S'] <= n && cnt['D'] <= n && cnt['F'] <= n 成立,说明当前窗口可以让剩余部分平衡。
    • 在满足条件的情况下,更新 ans 为当前的最小窗口长度,即 ans = Math.min(ans, right - left + 1)
    • 随后移动 left 向右以尝试缩小窗口,并将 cnt[s[left]] 加 1(恢复移除的字符计数)。

代码如下所示:

public static int solution(String input) {
    int len = input.length(); // 字符串长度
    int n = len / 4; // 每个字符应出现的频率
    char[] s = input.toCharArray(); // 将字符串转换为字符数组
    int left = 0; // 窗口左边界
    int ans = len; // 初始化最小窗口长度为整个字符串的长度
    int[] cnt = new int['X']; // 计数数组

    // 统计每个字符出现的次数
    for (char i : s) {
        cnt[i]++;
    }

    // 检查是否已经平衡
    if (cnt['A'] == n && cnt['S'] == n && cnt['D'] == n && cnt['F'] == n) {
        return 0; // 已经平衡,无需替换
    }

    // 滑动窗口
    for (int right = 0; right < len; right++) {
        cnt[s[right]]--; // 右边界字符移入窗口,减少它的计数
        // 检查当前窗口是否满足平衡条件
        while (cnt['A'] <= n && cnt['S'] <= n && cnt['D'] <= n && cnt['F'] <= n) {
            ans = Math.min(ans, right - left + 1); // 更新最小子串长度
            cnt[s[left++]]++; // 移动左边界,恢复移出窗口的字符计数
        }
    }

    return ans; // 返回结果
}

总结

本题通过滑动窗口快速调整并判断子串长度。使用计数数组追踪字符出现频率,确保窗口内满足条件时更新结果。要能准确识别出子数组和最值所采用的方法是什么。MarsCode AI在这一过程中提供了许多的帮助。

在做这一题的过程中,MarsCode AI工具会提供解题提示(如思路提示,代码提示,检查代码等),并在解题后立即给出分析,包括解题思路的优化建议。在遇到不懂的地方还可以通过反复提问来理清自己的思路。还能生成详细的解题步骤和相关知识点的讲解。我们能有效避免重复犯错,并掌握相似题型的解题方法。MarsCode AI在学习中的独特价值在于,它们能帮助用户快速定位核心知识点,减少前期因不熟练而带来的不必要的思考。

同时,利用MarsCode AI,我们可以在做题思路不畅的情况下,通过思路提示,降低当前的思考难度,在明确的思路下进行代码的编写。还可以通过代码提示,结合已学过的知识,进行这一题目代码框架的搭建,通过完善关键步骤来完成这道题目。使用AI工具检查代码,可以快速定位代码中的问题,比如数组越界等。并通过这一报错来思考解决办法。总之,通过使用MarsCode AI工具,刷题的效率和质量都有了大幅的提升。