文章目錄
Leetcode 560.和爲 K 的子數組
問題描述
給定一個整數數組和一個整數 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;
}
};
Leetcode 1248. 統計「優美子數組」
問題描述
給你一個整數數組 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;
}
};
Leetcode 523. 連續的子數組和
問題描述
給定一個包含非負數的數組和一個目標整數 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
的取餘存在哈希表中即可。
實現代碼
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;
}
};
Leetcode 1124. 表現良好的最長時間段
問題描述
給你一份工作時間表 hours,上面記錄着某一位員工每天的工作小時數。
我們認爲當員工一天中的工作小時數大於 8 小時的時候,那麼這一天就是「勞累的一天」。
所謂「表現良好的時間段」,意味在這段時間內,「勞累的天數」是嚴格 大於「不勞累的天數」。
請你返回「表現良好時間段」的最大長度。
示例 1:
輸入:hours = [9,9,6,0,6,6,9]
輸出:3
解釋:最長的表現良好時間段是 [9,9,6]。
提示:
- 1 <= hours.length <= 10000
- 0 <= hours[i] <= 16
解題報告
將工作時大於 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;
}
};
Leetcode 1371. 每個元音包含偶數次的最長子字符串
問題描述
給你一個字符串 s ,請你返回滿足以下條件的最長子字符串的長度:每個元音字母,即 ‘a’,‘e’,‘i’,‘o’,‘u’ ,在子字符串中都恰好出現了偶數次。
示例 1:
輸入:s = “eleetminicoworoep”
輸出:13
解釋:最長子字符串是 “leetminicowor” ,它包含 e,i,o 各 2 個,以及 0 個 a,u 。
解題報告
將每個元音出現次數的 奇偶性 作爲累加的 前綴和。
符合條件的區間中,區間 [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