第七十五天 --- 力扣424、284(2021/10/5每日一题)
题目一
力扣:424
思路
k==0特别情况
我们乍一看这题,会一下蒙住,关键问题就是有k的存在,可以替换掉一部分字符,那么替换掉谁,在哪里替换都是问题,所以,干脆,上来咱们先考虑特殊情况,即k=0。那么这个问题就转化成为了,在一个目标串上找一子串,其内部字符必须一致,且子串长度最大。这样就很好想了,我们只需要两个指针,扫描一遍字符串,动态统计最大长度即可。
k!=0的一般情况:双指针(滑动窗口)
我们有了上述特殊情况解法,推广到一般解法的时候,总体想法不会改变,只是会改变一些细节,我们还是采用两个指针,上面的方法也进行相应改进。
1、初始时,让窗口尽可能的大。这里其实就要看怎么理解这个k了,他是给我们可以换多少个任何字符名额,我们还是从特殊情况那块来说,当从头开始扩展窗口的时候,一旦遇到了和主体不一样的字符的时候,我为了让窗口尽可能的大,我给他换了,用k的名额,直到k名额用完了,再遇到和主体元素(个数最多的元素)不同的,此时容错的k已经没了,所以窗口初始时已经撑到初始时候最大了,不能扩展了,所以左指针向右走,是当前最优解了。
2、此时已经得到局部最大的窗口了,每一次就是拿这个最大的窗口框住一部分区间,右指针向右走一步,尝试继续扩展,如果扩展满足题意(总长度-窗口内个数最多元素个数<=k),那么窗口成功扩大,左指针不动,产生了一个新的局部最优解,不符合题意,说明窗口已经最大了,那么左指针为了让窗口“活下去”,所以向右走一位,继续维持局部最优窗口大小。
3、窗口大小就这样动态维护,到最后了一定是最优值
Sum Up:
<1>、这个思路最本质就是一个动态维护,在初始的时候给他撑到最大,产生局部最优,之后就在此基础上,每进入了一个新的一段串,在此窗口基础上,尝试继续扩展,成功了,产生局部最优,又有右指针右移,不成功,窗口整体右移。
<2>k要理解好,他就是给我们作弊的机会,和主体不一样没关系,我给你改,这个就相当于在动态维护窗口时候的一个条件罢了。
<3>最重要的:什么时候使用双指针-滑动窗口呢?当在一个大的串上,让你求一个区间,区间内满足某些条件,最终要这个子串最长,一定首选滑动窗口!
代码
注:
1、这里建议采用哈希表来记录滑动窗口内元素出现个数,但是这里根据题意就最多26个字母,所以建议使用vector来模拟真正的hash表(即固定个数且可以连续编码的元素建议使用vector模拟hash表),这样可以省去建立unordered_map的时间。
2、窗口总长:right-left+1
3、窗口内个数最多元素个数:这个玩意动态维护,一定记住,个数最多的元素真就不一定特地只是谁,当心来一个元素,该种元素出现个数++,看是否更新最多元素,所以它只代表主体元素个数,具体是哪个元素不考虑!这也是在求某些最多的…一种方法,动态维护,来一个新的时候,原本的个数产生了变化,再动态更新它,不针对某一个特定值。
class Solution {
public:
int characterReplacement(string s, int k) {
vector<int> num(26);//哈希表维护窗口内元素出现个数
int n = s.size();
int left = 0, right = 0;//滑动窗口左右指针
int cnt = 0;//窗口内出现最多的元素个数,便于检验当前窗口是否,满足题意
while (right < n) {
num[s[right] - 'A']++;
cnt = max(cnt, num[s[right] - 'A']);//进来新元素,更新cnt,cnt牛逼之处就在无需知道具体是谁,只要最大就行
if (right - left + 1 - cnt > k) {
//发现异于主体元素的个数过多,>k,无法替代,则当前窗口作废
if (num[s[left] - 'A'] == cnt) {
//小细节,注意移除的元素对cnt影响
cnt--;
}
num[s[left] - 'A']--;
left++;
}
right++;
}
return right - left;//因为循环最后right==n,多走了一次,所以直接做差即为答案
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
题目二
力扣:284
思路
直接模拟
代码
直接按照题意模拟
class PeekingIterator : public Iterator {
public:
PeekingIterator(const vector<int>& nums) : Iterator(nums) {
//初始化
n = nums.size();
point = 0;
tmp = nums;
}
// Returns the next element in the iteration without advancing the iterator.
int peek() {
return tmp[point];
}
// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
int next() {
int tmp1 = 0;
if (point > n - 1) {
//防止越界
tmp1 = tmp[n - 1];
point++;
return tmp1;
}
return tmp[point++];
}
bool hasNext() const {
if (point > n - 1) {
return false;
}
return true;
}
private:
int point;//当前指针
int n;//元素个数
vector<int> tmp;
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):