424. 替换后的最长重复字符 ●●

424. 替换后的最长重复字符 ●●

描述

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

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

示例

输入:s = “AABABBA”, k = 1
输出:4
解释:
将中间的一个’A’替换为’B’,字符串变为 “AABBBBA”。
子串 “BBBB” 有最长重复字母, 答案为 4。

题解

1. 双指针 1

用一个一维数组存储窗口内各字母的出现次数,每次以左边界上的字母为固定字母(即不同字母替换成该字母),右指针逐个往右移动,当无法再替换时,左指针右移,并更新当前的固定字母;

注意,因为本解法每次都以左边界字母为固定字母,所以还需要考虑左边界之前的字母被替换后的长度,

比如 s = “ACBBB”,k = 2;正确结果为 5;若不考虑 front ,则结果为 3。

class Solution {
public:
    int characterReplacement(string s, int k) {
        vector<int> cnts(26, 0);
        int n = s.length(), ans = 0;
        int l = 0, rest = k;
        cnts[s[0]-'A'] = 1;				// 初始左边界
        for(int r = 1; r < n; ++r){
            if(s[r] != s[l]) --rest;	// 替换操作,--rest
            ++cnts[s[r]-'A'];			// 统计频次
            while(rest < 0){			// 替换失败
                --cnts[s[l]-'A'];		
                ++l;					// 移动左指针
                rest = k - ((r-l+1) - cnts[s[l]-'A']));	// 更新固定字母为左边界字母,(r-l+1)-cnts[s[l]-'A'] 表示当前窗口下需替换字母的个数
            }
            int front = min(rest, l);		// 考虑替换前面的字母
            ans = max(ans, r-l+1+front);	// 更新最大值
        }
        return ans;
    }
};

2. 双指针 2

枚举字符串中的每一个位置作为右端点,然后找到其最远的左端点的位置,满足该区间内除了出现次数最多的那一类字符之外,剩余的字符(即非最长重复字符)数量不超过 k 个。

这样我们可以想到使用双指针维护这些区间,每次右指针右移,如果区间仍然满足条件,那么左指针不移动,否则左指针至多右移一格,保证区间长度不减小(保持不变或增大)

虽然这样的操作会导致部分区间不符合条件,即移动左指针后,该区间内非最长重复字符超过了 k 个(maxSame发生改变但可能未更新)。
但是这样的区间也同样不可能对答案产生贡献(直到移动右指针出现频次更高的字母)。当我们右指针移动到尽头,左右指针对应的区间的长度必然对应一个长度最大的符合条件的区间。

每次区间右移,我们更新右移位置的字符出现的次数,然后尝试用它更新重复字符出现次数的历史最大值,最后我们使用该最大值计算出区间内非最长重复字符的数量,以此判断左指针是否需要右移即可。

class Solution {
public:
    int characterReplacement(string s, int k) {
        vector<int> cnts(26, 0);
        int n = s.length();
        int l = 0, r = 0, maxSame = 0;
        for(; r < n; ++r){
            ++cnts[s[r]-'A'];
            maxSame = max(maxSame, cnts[s[r]-'A']);	// 更新最大频次,出现更大频次时才有效
            if(k < (r-l+1) - maxSame){		        // 替换失败
                --cnts[s[l]-'A'];			        // 移动左指针,缩小窗口(最大总长度此时不变)
                ++l;
            }
        }                                           // for 循环中 r 多走一步,结束时 r = n
        return r-l;							        // 窗口大小保证不减小,最大符合条件的窗口长度
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值