Given a list of non-negative numbers and a target integer k, write a function to check if the array has a continuous subarray of size at least 2 that sums up to the multiple of k, that is, sums up to n*k where n is also an integer.
Example 1:
Input: [23, 2, 4, 6, 7], k=6 Output: True Explanation: Because [2, 4] is a continuous subarray of size 2 and sums up to 6.
Example 2:
Input: [23, 2, 6, 4, 7], k=6 Output: True Explanation: Because [23, 2, 6, 4, 7] is an continuous subarray of size 5 and sums up to 42.
Note:
- The length of the array won't exceed 10,000.
- You may assume the sum of all the numbers is in the range of a signed 32-bit integer.
這道題給了我們一個數組和一個數字k,讓我們求是否存在這樣的一個連續的子數組,該子數組的數組之和可以整除k。遇到除法問題,我們肯定不能忘了除數為0的情況等處理。還有就是我們如何能快速的遍歷所有的子數組,並且求和,我們肯定不能完全的暴力破解,這樣OJ肯定不答應。我們需要適當的優化,如果是刷題老司機的話,遇到這種求子數組或者子矩陣之和的題,應該不難想到要建立累加和數組或者累加和矩陣來做。沒錯,這道題也得這么做,我們要遍歷所有的子數組,然后利用累加和來快速求和。在得到每個子數組之和時,我們先和k比較,如果相同直接返回true,否則再判斷,若k不為0,且sum能整除k,同樣返回true,最后遍歷結束返回false,參見代碼如下:
解法一:
class Solution { public: bool checkSubarraySum(vector<int>& nums, int k) { for (int i = 0; i < nums.size(); ++i) { int sum = nums[i]; for (int j = i + 1; j < nums.size(); ++j) { sum += nums[j]; if (sum == k) return true; if (k != 0 && sum % k == 0) return true; } } return false; } };
下面這種方法用了些技巧,那就是,若數字a和b分別除以數字c,若得到的余數相同,那么(a-b)必定能夠整除c。這里就不證明了,博主也不會證明。明白了這條定理,那么我們用一個集合set來保存所有出現過的余數,如果當前的累加和除以k得到的余數在set中已經存在了,那么說明之前必定有一段子數組和可以整除k。需要注意的是k為0的情況,由於無法取余,我們就把當前累加和放入set中。還有就是題目要求子數組至少需要兩個數字,那么我們需要一個變量pre來記錄之前的和,我們每次存入set中的是pre,而不是當前的累積和,參見代碼如下:
解法二:
class Solution { public: bool checkSubarraySum(vector<int>& nums, int k) { int n = nums.size(), sum = 0, pre = 0; unordered_set<int> st; for (int i = 0; i < n; ++i) { sum += nums[i]; int t = (k == 0) ? sum : (sum % k); if (st.count(t)) return true; st.insert(pre); pre = t; } return false; } };
既然set可以做,一般來說用哈希表也可以做,這里我們建立余數和當前位置之間的映射,由於有了位置信息,我們就不需要pre變量了,之前用保存的坐標和當前位置i比較判斷就可以了,參見代碼如下:
解法三:
class Solution { public: bool checkSubarraySum(vector<int>& nums, int k) { int n = nums.size(), sum = 0; unordered_map<int, int> m{{0,-1}}; for (int i = 0; i < n; ++i) { sum += nums[i]; int t = (k == 0) ? sum : (sum % k); if (m.count(t)) { if (i - m[t] > 1) return true; } else m[t] = i; } return false; } };
參考資料:
https://discuss.leetcode.com/topic/80975/java-solution
https://discuss.leetcode.com/topic/80793/java-o-n-time-o-k-space/2
https://discuss.leetcode.com/topic/80892/concise-c-solution-use-set-instead-of-map