枚举右,维护左高级篇
参考资料来源灵神在力扣所发的题单,仅供分享学习笔记和记录,无商业用途。
核心思路:参考枚举右,维护左基础篇-CSDN博客
力扣题单练习(灵神题单中摘取题目)
1010. 总持续时间可被 60 整除的歌曲
核心思路:
遍历数组,针对每个元素,查看哈希表中存在多少个元素能和它配对,使得它们的和能被60整除
利用哈希表记录当前元素之前所有元素对60取余后的结果及其出现的次数
class Solution {
public:int numPairsDivisibleBy60(vector<int>& time) {// 核心思路:遍历数组,针对每个元素,查看哈希表中存在多少个元素能和它配对,使得它们的和能被60整除// 利用哈希表记录当前元素之前所有元素对60取余后的结果及其出现的次数unordered_map<int, int> map;int ret = 0; // 用于存储符合条件的数对数量for (int i = 0; i < time.size(); i++) {// 计算当前元素对60取余的结果int x = time[i] % 60;// 查找哈希表中是否存在与当前余数配对的数// 若存在,那么当前元素能和之前出现过的这些元素分别组成满足条件的数对// (60 - x) % 60的作用是处理x为0的情况,保证当x为0时,配对的数也是0ret += map[(60 - x) % 60];map[x]++;}return ret;}
};
3185. 构成整天的下标对数目 II
核心思路:类似于1010. 总持续时间可被 60 整除的歌曲
class Solution {
public:long long countCompleteDayPairs(vector<int>& hours) {//核心思路:类似于1010. 总持续时间可被 60 整除的歌曲int time[24]={0};long long ret=0;for(int i=0;i<hours.size();i++){//有两个值模除24等于0:0,24的倍数。但是它们在维护区间都找的是24,而0或24的倍数在维护区间内对应的键是0。//所以还需要在模除24消除特殊化影响,其他数据不受影响ret+=time[(24-hours[i]%24)%24];time[hours[i]%24]++;}return ret;}
};
2748. 美丽下标对的数目
核心思路:遍历nums,用数组维护左区间元素以元素的第一个数字为区分统计数量
最大公因数,也被称作最大公约数
int gcd(int x, int y){return y==0?x:gcd(y,x%y);}
class Solution {
public://最大公因数,也被称作最大公约数int gcd(int x, int y){return y==0?x:gcd(y,x%y);}//获取第一个数字int word(int x){int ret=0;while(x/10) x/=10;return x;}int countBeautifulPairs(vector<int>& nums) {//核心思路:遍历nums,用数组维护左区间元素以元素的第一个数字为区分统计数量int m[10]={0};int ret=0;for(int i=0;i<nums.size();i++){int x=word(nums[i]);//枚举1~9查看是否和当前元素的最后一个数字互质,互质则统计结果for(int k=1;k<10;k++) if(gcd(k,nums[i]%10)==1) ret+=m[k];m[x]++;}return ret;}
};
2506. 统计相似字符串对的数目
核心思路:枚举右,维护左:采用状态压缩将字符串转化成数字形式
比较两个字符串的字符集合是否相同:
for(auto x:s) mark|=1<<(x-'a');
class Solution {
public:int similarPairs(vector<string>& words) {//枚举右,维护左:采用状态压缩将字符串转化成数字形式int ret=0;unordered_map<int,int> map;for(auto s:words){int mark=0;for(auto x:s) mark|=1<<(x-'a'); //将字符串转化成数字,去重,并且保证相同字符不同顺序、不同数量组成的字符串结果一致ret+=map[mark]++;}return ret;}
};
2874. 有序三元组中的最大值 II
核心思路:枚举右,维护左:维护两次
维护nums[i]最大值和维护i<j并且在区间内(nums[i] - nums[j])选取最大值
class Solution {
public:long long maximumTripletValue(vector<int>& nums) {//核心思路:枚举右,维护左:维护两次long long ret=0;int buff=max(nums[0],nums[1]);vector<int> ans;ans.push_back(nums[0]-nums[1]);//维护i<j并且在区间内(nums[i] - nums[j])选取最大值for(int i=2;i<nums.size();i++){ans.push_back(max(ans.back(),buff-nums[i])); //维护当前区间组合和前区间组合选取最大值buff=max(buff,nums[i]); //维护nums[i]最大值}//枚举k,从下标2开始。采用前期维护好的i<j区间内最大值*nums[k]从而获取满足条件的最大值for(int i=2;i<nums.size();i++) ret=max(ret,(long long)ans[i-2]*nums[i]);return ret;}
};
1031. 两个非重叠子数组的最大和
题意:
选出一个长度为firstLen的子数组和一个长度为secondLen的子数组,两个子数组位置随意并且不重叠
核心思路:进行两次枚举右,维护左。
第一次维护遍历过的区间firstLen长度的最大和子数组
第二次维护遍历过的区间secondLen长度的最大子数组
为什么不会漏解?
全覆盖两种相对位置:通过两次遍历,分别处理了两种所有可能的相对位置关系,确保不会有任何一种组合被遗漏。
动态维护左边最大和:在每次遍历过程中,利用变量 buff 动态地维护了左边子数组的最大和,从而能够在 O (1) 时间内获取到最大和,避免了重复计算。
滑动窗口思想:运用滑动窗口的方式,让右边的子数组逐步向右滑动,同时动态更新左边子数组的最大和,这样可以保证遍历到所有合法的组合。
class Solution {
public:int maxSumTwoNoOverlap(vector<int>& nums, int firstLen, int secondLen) {//题意:选出一个长度为firstLen的子数组和一个长度为secondLen的子数组,两个子数组位置随意并且不重叠//核心思路:进行两次枚举右,维护左。//为什么不会漏解?//全覆盖两种相对位置:通过两次遍历,分别处理了两种所有可能的相对位置关系,确保不会有任何一种组合被遗漏。//动态维护左边最大和:在每次遍历过程中,利用变量 buff 动态地维护了左边子数组的最大和,从而能够在 O (1) 时间内获取到最大和,避免了重复计算。//滑动窗口思想:运用滑动窗口的方式,让右边的子数组逐步向右滑动,同时动态更新左边子数组的最大和,这样可以保证遍历到所有合法的组合。vector<int> ans(nums.size()+1,0);for(int i=0;i<nums.size();i++) ans[i+1]=ans[i]+nums[i]; //前缀和int ret=0,buff=ans[firstLen];//第一次维护遍历过的区间firstLen长度的最大和子数组for(int i=firstLen;i<=nums.size()-secondLen;i++){int x=ans[i+secondLen]-ans[i];ret=max(ret,buff+x);buff=max(buff,ans[i+1]-ans[i-firstLen+1]);}buff=ans[secondLen];//第二次维护遍历过的区间secondLen长度的最大子数组for(int i=secondLen;i<=nums.size()-firstLen;i++){int x=ans[i+firstLen]-ans[i];ret=max(ret,buff+x);buff=max(buff,ans[i+1]-ans[i-secondLen+1]);}return ret;}
};
枚举右,维护左2000分一下题单刷完,后续会更新2000+的高级篇题单