题意:
给定一个大写字母组成的字符串和一个整数k。k代表可以替换字符串中的k个及以内字符为任意字符,求可能形成的最长全等子串(每个字符都相同)的长度。
分析:
我们区间的起点不定,终点不定(长度不定)遍历是没有办法遍历的,继续思考问题,画一下图,去研究我们要找的区间是什么样的呢?发现区间里面有很多相同的,其中不同的有k个。那么这个区间是满足要求的,其实我们要找的一个满足要求的区间就是区间尽可能长,且只包含小于等于k个不同字符。由于字符数是确定的,所以思路是用map存储字符对应一个list,list存放该字符出现的角标,再这个list里面找满足要求的最长区间。但是写完发现超时了。为什么会超时呢,代表解法存储了很多不必要的信息和基于此对信息的操作,一看那个map里的list存储了那么多角标还要遍历,就知道这里很可能很耗时而且多余。去看一下讨论区,看见很多人都提到滑窗,其实已经不必继续往下看了,这已经很明显了,我们用左右界确定一个区间,区间遍历整个数组,右边尽量扩大,不满足的时候左边就前进,我们动态的描述一个区间,判断区间是否满足要求的思路是一样的,那么如何知道区间有多少个相同字符呢?我们可以不关心到底是什么字符,只需要遍历的时候动态更新。
小结:很多时候算法时空消耗大,说明有不必要的数据和对数据的操作。这个时候需要反思思路,优化算法,而往往,我们要去思考是不是有的数据我们可以在遍历的时候动态的维护而不用存储起来再操作。比如,我们之前的算法是用list存储角标,是为了找出相同字符的个数,但是新的思路只需要遍历的时候不断更新即可。
public class Solution {
public int characterReplacement(String s, int k) {
Map<Character, Integer> map = new HashMap<>();
int maxc = 0;
int max = 0;
int left = 0;
char[] cs = s.toCharArray();
for(int right=0; right<cs.length; right++){
if(map.containsKey(cs[right])){
map.put(cs[right], map.get(cs[right])+1);
}else{
map.put(cs[right],1);
}
maxc = Math.max(maxc, map.get(cs[right]));
int len = right-left+1;
if(len - maxc <= k){ //区间总数-区间里最多的相同的字符数目<=k , 区间满足要求
max = Math.max(max, len);
}else{ //区间不满足要求(太大)就把区间左界右移
map.put(cs[left], map.get(cs[left])-1);
left++;
}
}
return max;
}
}