总持续时间可被 60 整除的歌曲

在歌曲列表中,第  i  首歌曲的持续时间为  time[i]  秒。
返回其总持续时间(以秒为单位)可被  60  整除的歌曲对的数量。
形式上,我们希望索引的数字  i  和  j  满足    i  <  j  且有  (time[i]  +  time[j])  %  60  ==  0。

示例  1:
        输入:[30,20,150,100,40]
        输出:3
        解释:这三对的总持续时间可被  60  整数:
        (time[0]  =  30,  time[2]  =  150):  总持续时间  180
        (time[1]  =  20,  time[3]  =  100):  总持续时间  120
        (time[1]  =  20,  time[4]  =  40):  总持续时间  60
示例  2:

        输入:[60,60,60]
        输出:3
        解释:所有三对的总持续时间都是  120,可以被  60  整数。
提示:
        1  <=  time.length  <=  60000
        1  <=  time[i]  <=  500

自我解答

        和  twoSum  很像,不一样的是这里的  target  有很多个,于是我尝试把它们都枚举出来。
        sum_times  =  [60*i  for  i  in  range(1,  17)]
        然后按照  twoSum  的思路进行,  时间复杂度  O(n*m)

class Solution:
    def numPairsDivisibleBy60(self, time: List[int]) -> int:
        sum_times = [60 * i for i in range(1, 17)]
        num_pairs = 0
        time_map = {time[0]: 1, }
        for t in time[1:]:
            for st in sum_times:
                num_pairs += time_map.get(st - t, 0)
            if t in time_map:
                time_map[t] += 1
            else:
                time_map[t] = 1

        return num_pairs

        提交结果:执行用时:484  ms,  在所有  Python3  提交中击败了  7.13%的用户。看来还不够

题解

        官方题解整体思路也是  "twoSum",  但运用了数学余数规律,避免了枚举的过程,从而让时间复杂度降低到  O(n)。
        那么数学余数有什么规律呢?  我们就拿    t  %  60  为例。
                若  1  <=  t  <  60,  则  余数  =  t,  如  45  %  60  =  45,  因为  t  =  45  +  60    0
                若  t  =  60,    余数  =    0,    因为  t  =  0  +  60
                若  t  =  62,    余数  =    2,    因为  t  =  2  +  60  
  1
                若  t  =  164,  余数  =  44,    因为  t  =  44  +  60    2
                若  t  =  220,  余数  =  40,    因为  t  =  40  +  60  
  3
        所以不管  t  多大,  都是  余数  +  60    n,  它们有相似的部分  60    n,  而且  余数范围永远是  [0,  59]

        我们再拿  t  =  135  举例:
                我们知道  135  %  60  =  15,  即  15  +  60    2,  那么加多少后,能整除  60  呢?
                可以      15  +  45  +  60  
  2  =  180
                也可以  15  +  45  +  60    3  =  240
                也可以  15  +  45  +  60  
  4  =  300
                ....
            也就是说,我们只需要加  45  +  60  *  n  就行了,即  只要数对  60  取余  是  45  就行了。

        所以,对于  t  =  135  来说,我们可以记录下  45  这名  “通缉犯”,  后面如果遇到  t  %  60  ==  45  的,就说明发现通缉犯了!
        和  twoSum  一样,使用哈希表来记录  "wanted",  如  wanted  =  {45:  1},  表示  45  被前面  1个人  “通缉”,
        当发现”通缉犯“时,表示被  1  个人成功“通缉”,成功通缉数  +  1。
        如果再遇到  60  -  t  %  60  =  45  的,则  wanted[45]  +=  1,  45  被  “通缉”数  +1。

        还有种特殊情况,t  %  60  ==  0,  这种"被通缉的"就是  0

class Solution2:
    def numPairsDivisibleBy60(self, time: List[int]) -> int:
        wanted = [0] * 60
        num_pairs = 0
        for t in time:
            div = t % 60
            num_pairs += wanted[div]
            wanted[div if div == 0 else 60 - div] += 1
        return num_pairs

执行用时:244  ms,  在所有  Python3  提交中击败了  100.00%  的用户
内存消耗:17.2  MB,  在所有  Python3  提交中击败了  7.10%  的用户

原题链接:https://leetcode-cn.com/problems/pairs-of-songs-with-total-durations-divisible-by-60