JavaScript陣列及非陣列物件的深淺克隆詳解原理
給你一個整數陣列 nums 和一個整數 k。
如果某個 連續 子陣列中恰好有 k 個奇數數字,我們就認為這個子陣列是「優美子陣列」。
請返回這個陣列中「優美子陣列」的數目。
示例 1:
輸入:nums = [1,1,2,1,1], k = 3
輸出:2
解釋:包含 3 個奇數的子陣列是 [1,1,2,1] 和 [1,2,1,1] 。
示例 2:
輸入:nums = [2,4,6], k = 1
輸出:0
解釋:數列中不包含任何奇數,所以不存在優美子陣列。
示例 3:
輸入:nums = [2,2,2,1,2,2,1,2,2,2], k = 2
輸出:16
提示:
1 <= nums.length <= 50000
1 <= nums[i] <= 10^5
1 <= k <= nums.length
解法一:
首先假設陣列中有k個奇數,且陣列的左右兩端都是奇數,比如陣列1,2,2,1,1,k=3,這樣的陣列中,肯定只有1個子陣列滿足題意條件。即陣列本身。
現在考慮在陣列兩端新增一些偶數,比如2,1,2,2,1,1,2,k=3。這樣的陣列可以有4個子陣列滿足題意。即分別加上左邊的0,1個偶陣列成子陣列,加上右邊的0,1個偶陣列成子陣列。
觀察可以得知,在這種條件下,陣列中優美子陣列的個數為(左邊偶數個數+1)*(右邊偶數個數+1)
考慮更一般的情況,我們可以記錄一下陣列中每個奇數的縫隙直接插了多少個偶數。設陣列中奇數總個數為n,則有陣列even[],even[0]表示陣列最左端的偶數個數,even[1]表示第一個奇數和第二個奇數之間的偶數個數,even[n]表示陣列最後一個奇數右端的偶數個數。則第i個奇數左端有even[i-1]個偶數,第i+k個奇數右端有even[k]偶數。迴圈遍歷even陣列即得答案。
點選檢視程式碼
class Solution { public: int numberOfSubarrays(vector<int>& nums, int k) { vector<int> even; int evenCount = 0, res = 0; for(int i = 0; i < nums.size(); i++) { if(nums[i] % 2) { even.push_back(evenCount); evenCount = 0; if(i == nums.size() - 1) even.push_back(evenCount); } else if(i == nums.size() - 1){ evenCount++; even.push_back(evenCount); } else evenCount++; } for(int i = 0; i + k < even.size(); i++) res += (even[i] + 1) * (even[i + k] + 1); return res; } };
解法二:
這是leetcode官方的解法,用到字首和的思想。官方解釋很難懂,我的理解是,維護一個變數odd,存放到目前位置i為止陣列中出現奇數的個數。開一個cnt[]陣列,記錄陣列中奇數個數為odd的次數。比如,現在遍歷到nums[i],到i位置的奇數個數為odd,那麼我們就讓odd+1。i這個位置為終點時有多少子陣列滿足優美的條件呢?應該是cnt[odd - k](注意檢查odd - k>=0)。
點選檢視程式碼
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
vector<int> cnt(nums.size() + 1, 0);
int odd = 0, res = 0;
cnt[0] = 1;
for(int i = 0; i < nums.size(); i++)
{
odd += (nums[i] & 1);
res += (odd - k >= 0) ? cnt[odd - k] : 0;
cnt[odd] += 1;
}
return res;
}
};
兩種解法時間複雜度都為o(n)。空間複雜度都為o(n)。下圖中提交時間較早的是解法一。