题目描述
自己解法
思路:本问题中,我们不关心数组的值,只关心满足条件的次数,所以可以对原数组进行修改。需要意识到的一个规律是:如果两个数
a
,
b
a,b
a,b相加能被60整除,那么(a%60) + (b%60) = 60
,所以我们可以利用这个规律来简化杂乱的初始数组,将数组值范围约束在0~59
。
步骤:首先对初始列表的所有元素取余(%)60
,元素都落在0~59
范围内。建立一个字典time_dict
,键的取值范围是0~59(可能只包含部分数字),值是键在原数组中出现的次数。针对字典中键的分布,有两种情况,分别用ans_1,ans_2
来存储:
- 如果键是
0或30
,键的特点是它们内部两两成对,当值val
大于1时,ans_1
的值等于val*(val-1)/2
(组合数公式) - 其他情况即
1~29,31~59
,键的特点是与对称位置(如1与59)键凑成一对,对于当前key
,如果60-key
也存在于字典的键中,那么ans_2
的值等于这两个键对应的值相乘,循环结束ans_2要除以2
,因为对称的部分算了2
次。
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),对长度为 n n n的初始数组要做 n n n次取余操作;
- 空间复杂度: O ( 1 ) O(1) O(1),ans_1,ans_2,time_dict的长度不会随 n n n变化。
class Solution:
def numPairsDivisibleBy60(self, time: List[int]) -> int:
for i in range(len(time)):
time[i] %= 60
time_dict = {}
ans_1,ans_2 = 0,0
for val in time:
if val in time_dict.keys():
time_dict[val] += 1
else:
time_dict[val] = 1
for key,val in time_dict.items():
if (key == 0 or key == 30) and val > 1:
ans_1 += val*(val-1)/2
else:
if 60-key in time_dict.keys():
ans_2 += val * time_dict[60-key]
return int(ans_1 + ans_2/2)
题解区解法
参考链接:Java 【2ms 击败100.00%】
思路和我的一样,都是利用余数规律,不同之处是直接开了一个长度为60的数组,与我的方法对比,坏处是存储空间变大,因为字典键的范围可能没有覆盖0~59,数组长度固定为60,好处是后续遍历对称位置的键值很方便,时间性能好,Java代码:
public int numPairsDivisibleBy60(int[] time) {
int count = 0;
int[] seconds = new int[60];
for(int t : time) {
seconds[t % 60] += 1;
}
count += combination(seconds[30], 2);
count += combination(seconds[0], 2);
int i = 1, j = 59;
while(i < j) {
count += seconds[i++] * seconds[j--];
}
return count;
}
// 求组合数
public int combination(int n, int k) {
long result = 1;
for(int i = 1; i <= k; i++) {
result = result * (n - i + 1) / i;
}
return (int)result;
}