You are given a list of songs where the ith song has a duration of time[i]
seconds.
Return the number of pairs of songs for which their total duration in seconds is divisible by 60
. Formally, we want the number of indices i
, j
such that i < j
with (time[i] + time[j]) % 60 == 0
.
Example 1:
Input: time = [30,20,150,100,40]
Output: 3
Explanation: Three pairs have a total duration divisible by 60:
(time[0] = 30, time[2] = 150): total duration 180
(time[1] = 20, time[3] = 100): total duration 120
(time[1] = 20, time[4] = 40): total duration 60
Example 2:
Input: time = [60,60,60]
Output: 3
Explanation: All three pairs have a total duration of 120, which is divisible by 60.
Constraints:
1 <= time.length <= 6 * 104
1 <= time[i] <= 500
這道題說是給了一個歌曲列表的每首歌的播放時長,現在讓找出有多少對兒的歌曲,使得其播放時長之和是 60 的倍數,看到這里有沒有覺得很眼熟,沒錯,其實本質還是一道 Two Sum 的題,來跟着博主一起大聲念 ’平生不識 TwoSum,刷盡 LeetCode 也枉然‘。不過這里不是簡單的判斷兩數之和是否存在,而是要求出所有的符合題意的個數。由於數組中可能出現重復數字,所以需要統計出每個數字出現的個數。另外,這道題有一個很好的 trick,利用到了余數的性質,若兩個數之和可以被 60 整數,則對其先分別對 60 取余后,再相加之和,還是可以被 60 整數,這樣就可以把數字都縮小到 [0, 59] 的范圍內了。之后就是要找和可以被 60 整除的兩個數字了,經典的 Two Sum 的思路是先確定一個數字,然后用目標和減去當前這個數字,就是要在 HashMap 中查找是否存在。若是兩個數字不同,則總共的組合數就是兩個數字的出現次數直接相乘,但是假如兩個數字相同,則是個組合問題,比如在n個數字中任意選兩個數字的情況有多少種,為 n(n - 1) / 2
。這里兩個數字相同有兩種情況,一種是它們都是 60 的倍數,取余后都變成了0,另一種是它們都是 30,這樣加起來就是 60 的倍數,這兩種情況要單獨計算。還有,就是要用一個 HashSet 記錄已經處理過的數字,以免產生重復計算,參見代碼如下:
解法一:
class Solution {
public:
int numPairsDivisibleBy60(vector<int>& time) {
int res = 0;
unordered_set<int> visited;
unordered_map<int, int> timeCnt;
for (int t : time) ++timeCnt[t % 60];
for (auto &a : timeCnt) {
if (visited.count(a.first)) continue;
if (a.first % 60 == 0 || a.first == 30) {
res += (a.second - 1) * a.second / 2;
} else {
int target = 60 - a.first;
if (!timeCnt.count(target)) continue;
res += a.second * timeCnt[target];
visited.insert(target);
}
visited.insert(a.first);
}
return res;
}
};
其實有種更簡潔的寫法,不用在統計完了出現次數之后再計算,而是在統計的時候就直接累加了。這里沒有用 HashMap,而是直接用了一個大小為 60 的數組,上面提到了通過對 60 取余,可以把數字都縮小到 [0, 59] 的范圍內,對於每次遍歷到的數字,加上能和其配對的數字的出現次數,方法是用 600 減去當前數字,然后對 60 取余。然后累加對 60 取余的次數,參見代碼如下:
解法二:
class Solution {
public:
int numPairsDivisibleBy60(vector<int>& time) {
int res = 0;
vector<int> cnt(60);
for (int t : time) {
res += cnt[(600 - t) % 60];
++cnt[t % 60];
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1010
類似題目:
參考資料:
https://leetcode.com/problems/pairs-of-songs-with-total-durations-divisible-by-60/