给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。
一个连续区间的和为 k
意味着 preSum[j]-preSum[i]==k
,所以我们提前将前缀和放到哈希表中,然后在顺序遍历过程中直接查找当前的前缀和减去 k
是否存在然后累加出现次数即可。
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> mp;
mp[0] = 1;
int ans = 0, preSum = 0;
for (auto& x:nums) {
preSum += x;
if (mp.find(preSum - k) != mp.end()) ans += mp[preSum - k];
mp[preSum]++;
}
return ans;
}
};
给你一个整数数组 nums 和一个整数 k。
如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。
请返回这个数组中「优美子数组」的数目。
示例 1:
输入:nums = [1,1,2,1,1], k = 3
输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。
只记录奇数个数, preSum[j]
表示 nums[0:j]
这个区间奇数出现的次数。所以 某个 连续 子数组中恰好有 k 个奇数数字 意味着 preSum[j]-preSum[i]==k
,其他部分和上一题大差不差。
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int n = nums.size();
vector<int> count(n+1, 0);
count[0] = 1;
int res = 0, odd = 0;
for (int i = 0; i < n; ++i) {
odd += nums[i]&1;
if (odd >= k) res += count[odd-k];
count[odd]++;
}
return res;
}
};
给定一个包含非负数的数组和一个目标整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。
示例 1:
输入: [23,2,4,6,7], k = 6
输出: True
解释: [2,4] 是一个大小为 2 的子数组,并且和为 6。
根据题意,(preSum[j]-preSum[i])%k==0
,根据同余定理可知 preSum[i]%k==preSum[j]%k
。所以我们将前缀和对 k
的取余存在哈希表中即可。
本 题 中 的 数 组 是 非 负 的 \color{red}本题中的数组是非负的 本题中的数组是非负的
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
int sum=0;
unordered_map<int,int> map;
map.insert({0,-1});
for(int i=0;i<nums.size();i++){
sum+=nums[i];
if(k!=0)
sum=sum%k;
if(map.count(sum)){
if(i-map[sum]>1)
return true;
}
else
map.insert({sum,i});
}
return false;
}
};
给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
示例 1:
输入:hours = [9,9,6,0,6,6,9]
输出:3
解释:最长的表现良好时间段是 [9,9,6]。
提示:
将工作时大于 8
的那一天映射成 1
,将工作时小于等于 8
的那一天映射成 -1
。在哈希表中存该前缀和的下标。
表面上看 「表现良好时间段」的最大长度是子数组的和为 1
,但是这样理解是错误的。
0
开始累加的,所以直接取为 i+1
。class Solution {
public:
int longestWPI(vector<int>& hours) {
int ans=0,preSum=0;
unordered_map<int,int>mp;
for(int i=0;i<hours.size();i++){
preSum+=(hours[i]>8?1:-1);
if(preSum>0) {
ans=i+1;
continue;
}
if(mp[preSum-1]!=0)
ans=max(ans,i-mp[preSum-1]+1);
if(mp[preSum]==0) mp[preSum]=i+1;
}
return ans;
}
};
给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 ‘a’,‘e’,‘i’,‘o’,‘u’ ,在子字符串中都恰好出现了偶数次。
示例 1:
输入:s = “eleetminicoworoep”
输出:13
解释:最长子字符串是 “leetminicowor” ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。
这 道 题 真 的 是 太 巧 妙 了 。 \color{red}这道题真的是太巧妙了。 这道题真的是太巧妙了。
将每个元音出现次数的 奇偶性 作为累加的 前缀和。
符合条件的区间中,区间 [0,i]
中各元音字母出现次数的奇偶性必定和 [0,j]
中各元音字母出现次数的 奇偶性相同,只有这样,区间 [i,j]
中各元音字母才会出现偶数次。
那么问题就变成了如何累加 各个元音字母出现次数的 奇偶性 呢?
u
,第二位二进制数【从左到右】表示 o
,第三位二进制数【从左到右】表示 i
,第四位二进制数【从左到右】表示 e
,第五位二进制数【从左到右】表示 a
。00000
表示每个元音字母均出现偶数次;11111
表示每个元音字母均出现奇数次。前缀和
和该元音字母所对应的二进制位进行异或操作,得到一个新的 前缀和
。前缀和
状态,如果存在的话,更新答案,如果不存在的话,将该状态存入哈希表中。class Solution {
public:
int findTheLongestSubstring(string s) {
vector<int> pre(32,INT_MAX);
pre[0]=-1;
const int N=s.size();
int cur=0;
int ans=0;
for(int i=0;i<N;++i){
switch(s[i]){
case 'a':cur^=1;break;
case 'e':cur^=2;break;
case 'i':cur^=4;break;
case 'o':cur^=8;break;
case 'u':cur^=16;break;
default:break;
}
if(pre[cur]==INT_MAX) pre[cur]=i;
else ans=max(ans,i-pre[cur]);
}
return ans;
}
};
// 作者:mnizy
// 链接:https://leetcode-cn.com/problems/find-the-longest-substring-containing-vowels-in-even-counts/solution/jian-dan-de-si-lu-by-mnizy/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这里引入哈希表完全是为了减少时间消耗,以空间来换时间。
常规题目中,前缀和指的是一个区间的和;非常规题目中,前缀和指的是某个元素出现的次数;像最后一题中的高级题目中,使用二进制位来表示前缀和。
[1] Leetcode 560.和为 K 的子数组
[2] 哈希表优化系列【空间换时间】-Leetcode 560.和为 K 的子数组
[3] Leetcode 1248. 统计「优美子数组」
[4] Leetcode 1248. 统计「优美子数组」【记录奇数位置&滑动窗口&前缀和】
[5] 【每日算法Day 107】面试必考:良心推荐,一题三解,不看后悔一辈子
[6] 523. 连续的子数组和
[7] Leetcode 523. 连续的子数组和【前缀和+同余定理】
[8] 题解区:官方答案
[9] Leetcode 1124. 表现良好的最长时间段
[10] Leetcode 1371. 每个元音包含偶数次的最长子字符串
[11] Leetcode 1371 题解区:mnizy