最小替换子串长度

138 阅读2分钟

题目链接www.marscode.cn/practice/w7…

题目的要求是通过替换一个最小长度的连续子串,使得替换后整个字符串中 A、S、D、F 四个字符的出现次数相等。注意,题目不要求这个连续子串中的所有字符都被替换。

双指针 + 滑动窗口

代码

时间复杂度O(n)O(n)

空间复杂度O(1)O(1)

#include <iostream>
#include <string>
#include <array>
#include <algorithm>
using namespace std;

inline int charToIndex(const char c) {
    switch (c) {
        case 'A': return 0;
        case 'S': return 1;
        case 'D': return 2;
        default: return 3;
    }
}

int solution(string input) {
    const int length = static_cast<int>(input.size());
    const int targetFrequency = length >> 2; // 每个字符应该出现的理想次数

    // 计算多余数量,按理来说没有超过的targetFrequency的字符对应的excess值为0,但为负数也不会影响代码的正确性
    array<int, 4> excess;
    fill(excess.begin(), excess.end(), -targetFrequency);
    for (const char &ch: input) {
        ++excess[charToIndex(ch)];
    }

    // 不需要替换时每个字符的excess值为0
    if (excess[0] == 0 and excess[1] == 0 and excess[2] == 0 and excess[3] == 0) {
        return 0;
    }

    // 滑动窗口,寻找最短子串
    array<int, 4> currentWindow;
    fill(currentWindow.begin(), currentWindow.end(), 0);
    int left = 0;
    int minimalSubstringLength = length;

    for (int right = 0; right < length; ++right) {
        // 扩大窗口
        ++currentWindow[charToIndex(input[right])];

        // 当窗口满足包含所有多余字符数量时,尝试缩小窗口
        while (currentWindow[0] >= excess[0] and currentWindow[1] >= excess[1] and
               currentWindow[2] >= excess[2] and currentWindow[3] >= excess[3]) {
            // 更新最短子串长度
            minimalSubstringLength = min(minimalSubstringLength, right - left + 1);

            // 缩小左边界
            --currentWindow[charToIndex(input[left])];
            ++left;
        }
    }

    return minimalSubstringLength;
}

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

思路

统计每个字符的excess值,如果为全0,则不需要替换;否则用左右指针 [left, right] 做滑动窗口,去寻找最短的一个子串,使得这个子串中至少包含这四种字符多余的数量。当满足条件后,就尝试收缩 left 以找更短的子串。