Leecode题目
219. 存在重复元素 II
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
最小不超过k,相当于维护一个大小为K的容器,使它的元素不能重复,当然如果能重复那就说明有答案咯!
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_set<int> us;
for(int i = 0;i < nums.size();i++){
if(us.find(nums[i]) != us.end()) return true;
us.insert(nums[i]);
if(us.size() > k) us.erase(us.find(nums[i-k]));
}
return false;
}
};
220. 存在重复元素 III
给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。
示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1, t = 2
输出: true
示例 3:
输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
思路,与上一题的不同,在于,查找的值是一个范围,可以用二分查找法upper_bound()(大于等于的第一个值),注意你找的起始点应该是nums[i]-t ,这点很机智,实际上很多绝对值都是一个范围,满足范围里的数都可以!!!也就是说你满足这个范围的最小值,就是你查找的结果,只要它小于最大的范围值nums[i]+t 就可以了!!!
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
// 活动窗口
set<int> record;
for(int i=0; i<nums.size(); i++){
auto s = record.lower_bound((double)nums[i]-t); // 防溢出
if(s != record.end() && *s <= (double)nums[i]+t)
return true;
record.insert(nums[i]);
// record.size保持<=k+1
// 当record达到k+1时, 马上要删去一个, 为下一个元素腾出空间
if(record.size() == k+1)
record.erase(nums[i-k]);
}
return false;
}
};
239. 滑动窗口最大值
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
这次是真的滑动窗口了,实际上这题的方法却不是用的滑动窗口…
方法一:我想到的第一个方法是用multiset,维护一个大小为k的容器,然后不断取最大值。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
if(nums.size() == 0) return result;
multiset<int> ms(nums.begin(),nums.begin()+k);
result.push_back(*(ms.rbegin()));
for(int i = k;i < nums.size();i++){
ms.erase(ms.find(nums[i-k]));
ms.insert(nums[i]);
result.push_back(*(ms.rbegin()));
}
return result;
}
};
方法二:动态规划
我也不知道这算不算动态规划,
- 把所有数组划分成k大小的块
- 求每个块内从左边开始当前的最大值
- 求每个块右边开始当前的最大值
我说肯定说不清楚上图比较清楚
最后结果就是,如果滑动窗口在一个块里面,就不用说了。
如果滑动窗口在两个块里面,如下图,那么结果就是left[2]和right[4]比较!
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
if(nums.size() == 0) return result;
vector<int> left(nums.size(),0);
vector<int> right(nums.size(),0);
int nsize = nums.size();
for(int i = 0;i < nums.size();i += k){
int d = min(k,nsize);
int maxleft = nums[i];
int maxright = nums[i+d-1];
for(int j = 0;j < d;j++){
maxleft = max(maxleft,nums[i+j]);
maxright = max(maxright,nums[i+d-1-j]);
left[i+j] = maxleft;
right[i+d-1-j] = maxright;
}
nsize = nsize-k;
}
for(int i = k-1;i < nums.size();i++){
result.push_back(max(right[i-k+1],left[i]));
}
return result;
}
};
方法三:双端队列
参考键值offer,为维护一个滑动窗口的最大值,用双端队列保存,每加入一个值n,把之前小于当前n的值都出队。
如:
【352】461,k = 3
此时deque里面的值为52(因为在添加5的时候3就淘汰了),当加入4的时候,2就要先出队!然后还要维护一个下标队列,当最大值要出队的时候,就要重新更新最大值。代码就不上了。
424. 替换后的最长重复字符
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
示例 1:
输入:
s = “ABAB”, k = 2
输出:
4
解释:
用两个’A’替换为两个’B’,反之亦然。
示例 2:
输入:
s = “AABABBA”, k = 1
输出:
4
解释:
将中间的一个’A’替换为’B’,字符串变为 “AABBBBA”。
子串 “BBBB” 有最长重复字母, 答案为 4。
错误思维:
这题我当时没有头绪,后来想到的是用滑动窗口。
- 用i,j两个点来维护滑动窗口左边右边,然后从头开始遍历,以s[i]为字符的子队列
- j向后滑动,若是与s[i]相同,加入,若是不同,如果k>0同样可以加入,直达不满足上述条件。
- 当不满足上述条件时,i要开始滑动,滑动的不是下一个节点,而是不等于当前节点值的下一个节点,如AABA,i当前为0,下一个滑动为2,之后重复上述过程。
结果后来出现了报错:
当BAAA,k=1时,我的结果是3,实际上应该是4.
错误原因在于:
不应该以i的位置为滑动窗口子串的重复字符,要不断更新滑动窗口中滑动窗口子串的最大重复字符。
而且总结一下滑动窗口的经验,其实每个滑动窗口都应该有一个集合用来记录滑动窗口的状态。这题就应该记录的是每个字符在子串出现的次数,用这个记录,来更新最大重复字符!
正确解法:
首先明白这题子串满足的条件是:
当前子串出现重复字符最大值+k > 当前子串的长度
那么就是满足的。于是我们可以开始维护一个滑动窗口wd,起始为i ,结尾为j。
- 从头开始遍历,添加一个s[j+1]进去,若是满足上述条件,添加成功,若是不满足,则滑动窗口向右滑动。
- 且在添加或者滑动期间维护一个record【26】用来记录子串字符出现的次数,用于找最大重复字符。
- 注意更新的最大重复字符的时候,只与添加的s[j+1]字符和滑动时s[i]字符有关系!
代码:
class Solution {
public:
int characterReplacement(string s, int k) {
int ssize = s.size();
if (ssize == 0)return 0;
//int wsize = 1;
int i = 0;
int j = 0;
vector<int> record(26, 0);
char maxc = s[0];
record[s[i] - 'A'] = 1;
while (j < ssize-1) {
++j;
record[s[j] - 'A']++;
if (record[s[j] - 'A'] > record[maxc - 'A'])maxc = s[j];
if (j - i + 1 > record[maxc - 'A'] + k) {
record[s[i] - 'A']--;
++i;
if (record[s[j] - 'A'] > record[maxc - 'A'])maxc = s[j];
}
}
return j-i+1;
}
};
未完待续…