力扣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;
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_45678698/article/details/120616493