424. 替换后的最长重复字符(从暴力到滑动)

给你一个字符串 s 和一个整数 k 。你可以选择字符串中的任一字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。

在执行上述操作后,返回 包含相同字母的最长子字符串的长度。

示例 1:

输入:s = "ABAB", k = 2
输出:4
解释:用两个'A'替换为两个'B',反之亦然。
示例 2:

输入:s = "AABABBA", k = 1
输出:4
解释:
将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
子串 "BBBB" 有最长重复字母, 答案为 4。
可能存在其他的方法来得到同样的结果。

提示:

1 <= s.length <= 105
s 仅由大写英文字母组成
0 <= k <= s.length


https://leetcode.cn/problems/longest-repeating-character-replacement/solutions/586648/ti-huan-hou-de-zui-chang-zhong-fu-zi-fu-eaacp

证明1:如果长度为 L 的子串不符合题目的要求,那么左边界固定,长度更长的子串也不符合题目的要求。
答:记 count(X) 表示长度为 L 的子串中,字符 X 出现的次数。

不失一般性,假设长度为 L 的子串,出现最多的字符为 A,记 count(A)=x。其余字符均为 B,记 count(B)=y。由字符 A 出现次数最多,可知 x≥y。又由于长度为 L 的子串不符合题目的要求,可知 y>k。起点固定的情况下,考虑更长的子串:

如果接下来看到的字符都是 A(频数最多的字符越来越多),依然须要考虑把之前看到的 B 全部替换成为 A,由于 count(B)=y>k,这是不能做到的;
如果接下来看到的字符不是 A(频数较少的字符超过原来频数最多的字符),那么须要考虑把之前看到的 A 全部替换成为新的频数最多的字符,由于 count(A)=x≥y>k,这也是不能做到的。
说明:这里我们只讨论了滑动窗口扫过的子区间只含有 2 种字符的情况,如果滑动窗口扫过的子区间只含有 3 种以及 3 种以上字符,讨论是类似的。


显然有暴力,穷举左右端点

for(int i=0;i<n;i++){
	for(int j=i+1;j<n;j++){
		check(i,j);
	}
}

用证明1优化,内层循环j遇到不合法的字符串时 $[left,right']$ right' > 最后一个合法j 也不合法 ,可以直接跳过

for(int i=0;i<n;i++){
	for(int j=i+1;j<n;j++){
		if(!check(i,j)) break;
	}
}

退出内层循环后我们移动i到下一位置,但j不用回到i+1,因为更大大字串最少也是上轮j+1的位置

int j=0;
for(int i=0;i<n;i++){
	while(++j&&j<n){
		if(!check(i,j)) break
	}
}

具体实现

#include <bits/stdc++.h>

using namespace std;

class Solution {
public:
    int characterReplacement(string s, int k) {
        int maxLength = 0; // 用于存储最长子串的长度
        int count[26] = {0}; // 用于存储每个字符的出现次数
        int maxCount = 0; // 窗口内出现次数最多的字符的出现次数

        int n = s.length();
        // 对左指针穷举,j=-1 确保初始位置0的字符进入统计数组
        for (int i = 0, j = -1; j < n; i++) {
            // 使用 while 循环扩展右指针
            while (++j < n) {
                // 更新当前字符的计数
                count[s[j] - 'A']++;
                // 更新窗口内出现次数最多的字符的计数
                maxCount = max(maxCount, count[s[j] - 'A']);
                // 检查当前窗口是否满足条件
                if ((j - i + 1) - maxCount > k) {
                    // 如果不满足条件,跳出 while 循环
                    count[s[i] - 'A']--; // 左指针字符计数减少
                    break;
                }
                // 更新最长子串的长度
                maxLength = max(maxLength, j - i + 1);
            }
        }
        return maxLength;
    }
};

int main() {
    Solution solution;
    string s = "AABABBA";
    int k = 1;
    int result = solution.characterReplacement(s, k);
    cout << "最长子串的长度: " << result << endl; // 输出应为 4

    // 更多测试用例
    vector<pair<string, int>> testCases = {
        {"AABABBA", 1},
        {"ABAB", 2},
        {"AABBBCC", 2},
        {"AAAA", 0},
        {"A", 1},
        {"AAABBB", 3},
        {"BAAAB", 3},
        {"KRSCDCSONAM", 4}
    };

    for (const auto& testCase : testCases) {
        string input = testCase.first;
        int kVal = testCase.second;
        result = solution.characterReplacement(input, kVal);
        cout << "字符串: \"" << input << "\", k = " << kVal
             << " => 最长子串的长度: " << result << endl;
    }

    return 0;
}

posted @ 2025-03-26 19:53  丘狸尾  阅读(8)  评论(0)    收藏  举报