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

118 阅读4分钟

问题描述

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

测试样例

样例1:

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

样例2:

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

样例3:

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

样例4:

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

样例5:

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

问题分析:

我们要确保每个字符出现的频率都达到目标值 n / 4。如果当前已经平衡(即每个字符的频率都为 n / 4),则返回 0。否则,我们需要找到最小的子串长度,使得通过替换这个子串中的字符,可以使字符的频率达到目标。

1. 频率统计与目标设定:

  • 问题的关键: 我们的目标是使字符串中每个字符 'A', 'S', 'D', 'F' 的出现频率相等。根据题目要求,字符串的长度 n 是 4 的倍数,所以每个字符的目标频率为 n / 4
  • 思路: 我们首先统计整个字符串中每个字符的出现频率,并检查当前的频率是否已经符合目标(n / 4)。如果已经符合目标,就可以直接返回 0,因为不需要进行任何替换。

2. 滑动窗口与子串检查:

  • 子串的检查: 如果频率不平衡,我们需要寻找一个最小的子串,通过替换该子串中的字符,使得所有字符的频率达到目标值。
  • 滑动窗口: 我们需要遍历所有可能的子串。对于每个子串,计算其字符频率,并检查替换该子串后是否能让整体字符频率达到目标。为了高效地计算每个子串的字符频率,我们可以直接统计每个子串的频率,而不需要每次重新计算整个字符串的频率。

3. 频率更新与验证:

  • 更新频率: 对于每个子串,先统计它的字符频率(用一个哈希表 sub_freq)。然后,将这些频率从整个字符串的频率 freq 中扣除(使用 new_freq 来表示更新后的频率)。
  • 检查平衡: 检查更新后的频率是否满足目标要求:对于每个字符,检查它的频率是否超过了目标频率 n / 4。如果超过了,则当前子串无法达到平衡,继续检查下一个子串。

4. 早期终止:

  • 优化: 如果某个子串可以通过替换达到平衡,那么立即返回该子串的长度,因为我们正在寻找最小的子串长度。这可以避免不必要的计算,提升效率。

5. 时间复杂度考虑:

  • 对于长度为 n 的字符串,最多需要检查所有长度从 1n 的子串。对于每个子串,需要统计它的字符频率并验证平衡。因此,时间复杂度大约是 O(n^2),每次检查的复杂度为 O(n)。对于较小的输入规模,这个复杂度是可以接受的。

6. 边界情况处理:

  • 输入已经平衡: 如果输入字符串已经符合目标频率,我们应该立即返回 0。
  • 输入字符串不可平衡: 如果没有任何子串可以替换来平衡字符串(理论上不应该发生,但依然需要考虑),返回 -1

代码实现:

#include <iostream>
#include <string>
#include <unordered_map>

int solution(const std::string& input_str) {
    int n = input_str.size();
    int target = n / 4;  // 因为是4的倍数,所以每个字符应该出现 n / 4 次

    // 统计每个字符出现的次数
    std::unordered_map<char, int> freq;
    for (char c : input_str) {
        freq[c]++;
    }

    // 如果已经平衡,返回 0
    if (freq['A'] == target && freq['S'] == target && freq['D'] == target && freq['F'] == target) {
        return 0;
    }

    // 从小到大尝试子串长度
    for (int length = 1; length <= n; ++length) {
        // 遍历所有可能的起始位置
        for (int start = 0; start <= n - length; ++start) {
            // 统计子串的字符频率
            std::unordered_map<char, int> sub_freq;
            for (int i = start; i < start + length; ++i) {
                sub_freq[input_str[i]]++;
            }

            // 检查当前子串替换后是否能达到平衡
            bool valid = true;
            std::unordered_map<char, int> new_freq = freq;
            for (const auto& entry : sub_freq) {
                new_freq[entry.first] -= entry.second;
            }

            // 判断是否能平衡
            for (const auto& entry : new_freq) {
                if (entry.second > target) {
                    valid = false;
                    break;
                }
            }

            if (valid) {
                return length;
            }
        }
    }
    return -1;  // 如果没有找到合适的子串
}

int main() {
    std::cout << (solution("ADDF") == 1) << std::endl;
    std::cout << (solution("ASAFASAFADDD") == 3) << std::endl;
    std::cout << (solution("AAAADDDDAAAASSSS") == 4) << std::endl; 
    return 0;
}