题目:
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
注意:
字符串长度 和 k 不会超过 104。
示例 1:
输入: s = "ABAB", k = 2 输出: 4 解释: 用两个‘A‘替换为两个‘B‘,反之亦然。
示例 2:
输入: s = "AABABBA", k = 1 输出: 4 解释: 将中间的一个‘A‘替换为‘B‘,字符串变为 "AABBBBA"。 子串 "BBBB" 有最长重复字母, 答案为 4。
解答:
开始没想到滑动窗口,用dfs做了半天,第20几个用例答案不对,查错也查不出来。
由于题目是问最长子串,这种题目动态规划或者滑动窗口需要着重考虑一下,因为都是当前状态都是和之前相邻的状态有关。
1.先固定左边界,右边界一直右移,直到当前区间内的需要替换的字母数大于k。
2.之后左边界右移,直到该区间需要替换的字母数小于等于k。重复1步骤。
问题是怎么找出某个区间内需要替换的字母数。
答案是用一个memo数组记录各字母在当前区间内的出现次数,出现最多的,我们认为它就是主要字母,其他字母都替换为它。这样总的替换次数是最少的。(其实就是贪心。)
(我之前妈蛋就是这里没想明白怎么找,结果用dfs遍历所有可能,太菜了)
可行的代码:
1 class Solution { 2 public: 3 int characterReplacement(string s, int k) { 4 int n=s.size(); 5 if(n==0){return 0;} 6 int le=0,ri=0,max_cnt=0; 7 int res=1; 8 vector<int> cnt(26,0); 9 while(ri<n){ 10 cnt[s[ri]-‘A‘]+=1; 11 max_cnt=*max_element(cnt.begin(),cnt.end()); 12 while(ri-le+1-max_cnt>k){ 13 cnt[s[le]-‘A‘]-=1; 14 ++le; 15 max_cnt=*max_element(cnt.begin(),cnt.end()); 16 } 17 res=max(res,ri-le+1); 18 ++ri; 19 } 20 return res; 21 } 22 };
这个写法是对的,但是还有更优解。就是每次只有右边界更新的时候,max_cnt才进行更新。
左边界右移的时候不更新。
我自己也不是特别理解原理,只能贴一个解释:
这里有个优化,不需要每次都去重新更新max_count。比如说"AAABCDEDFG" k=2,这个case,一开始A出现3次,max_count=3,但是当指针移到D时发现不行了,要移动left指针了。此时count[‘A‘]-=1,但是不需要把max_count更新为2。为什么呢? 因为根据我们的算法,当max_count和k一定时,区间最大长度也就定了。当我们找到一个max_count之后,我们就能说我们找到了一个长度为d=max_count+k的合法区间,所以最终答案一定不小于d。所以,当发现继续向右扩展right不合法的时候,我们不需要不断地右移left,只需要保持区间长度为d向右滑动即可。如果有某个合法区间大于d,一定在某个时刻存在count[t]+1>max_count,这时再去更新max_count即可。
链接:https://leetcode-cn.com/problems/longest-repeating-character-replacement/comments/69360
代码优化为:
1 class Solution { 2 public: 3 int characterReplacement(string s, int k) { 4 int n=s.size(); 5 if(n==0){return 0;} 6 int le=0,ri=0,max_cnt=0; 7 int res=1; 8 vector<int> cnt(26,0); 9 while(ri<n){ 10 cnt[s[ri]-‘A‘]+=1; 11 max_cnt=max(max_cnt,cnt[s[ri]-‘A‘]); 12 while(ri-le+1-max_cnt>k){ 13 cnt[s[le]-‘A‘]-=1; 14 ++le; 15 } 16 res=max(res,ri-le+1); 17 ++ri; 18 } 19 return res; 20 } 21 };
原文地址:https://www.cnblogs.com/FdWzy/p/12423970.html