1. 程式人生 > >[LeetCode] Continuous Subarray Sum 連續的子陣列之和

[LeetCode] Continuous Subarray Sum 連續的子陣列之和

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:

    1. The length of the array won't exceed 10,000.
    2. 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;
    }
};

參考資料: