also 中等题 歌曲时长配对问题
问题描述
小F有一个由歌曲组成的列表,每首歌的持续时间用数组 time 表示,其中第 i 首歌的时长为 time[i] 秒。她想知道列表中有多少对歌曲的总持续时间能够被 10 整除。具体来说,我们希望找到所有满足 i < j 且 (time[i] + time[j]) % 10 == 0 的配对。
例如,给定 time = [3, 2, 15, 1, 4],有一对歌曲(time[2] 和 time[4])的时长和为 19 秒,能够被 10 整除。
测试样例
样例1:
输入:time = [3, 2, 15, 1, 4]
输出:0
样例2:
输入:time = [10, 20, 30]
输出:3
样例3:
输入:time = [7, 3, 5, 5, 10]
输出:2
1. 胡乱分析
给定一个列表 time = [t1, t2, ..., tn],我们要找出所有 (i, j) 对使得:
i < j(time[i] + time[j]) % 10 == 0
注意到这里并不需要计算每一对歌曲的总时长,而是关注它们的总时长是否能被 10 整除。
2. 余数分类
我们首先可以将所有的歌曲时长对 10 取余,得到每首歌的余数(即 time[i] % 10)。两个时长相加能被 10 整除的条件是它们的余数之和为 10 或 0。具体来说:
- 如果
time[i] % 10 == r1,那么另一首歌的时长需要满足time[j] % 10 == (10 - r1) % 10,才能使得(time[i] + time[j]) % 10 == 0。
因此,我们可以用一个数组来记录每个余数的歌曲数量,从而避免暴力枚举所有对。
3. 方案
- 遍历
time数组,对于每首歌的时长,计算其对 10 的余数。 - 用一个大小为 10 的计数数组
count来记录每个余数出现的次数。count[r]表示余数r的歌曲数量。 - 对于每个新的时长,我们可以根据其余数来查找能够与之配对的歌曲数量,并更新计数数组。
- 处理完所有歌曲后,返回满足条件的配对总数。
4. 具体步骤
- 初始化一个大小为 10 的计数数组
count,初始值为 0。 - 遍历每首歌的时长,对其取余,计算可以形成合法对数的数量。
- 在遍历过程中,更新
count数组,记录每个余数的歌曲数量。
5. 时间复杂度分析
- 遍历一次
time数组,时间复杂度是 O(n),其中 n 是time数组的长度。 - 对每首歌,计算其余数并更新计数数组,因此整体时间复杂度是 O(n)。
案例分析
示例 1
输入:time = [3, 2, 15, 1, 4]
- 歌曲的余数分别是
[3, 2, 5, 1, 4]。 - 检查所有可能的配对:
- (3, 2): 3 + 2 = 5, 不能被 10 整除。
- (3, 5): 3 + 5 = 8, 不能被 10 整除。
- (3, 1): 3 + 1 = 4, 不能被 10 整除。
- (3, 4): 3 + 4 = 7, 不能被 10 整除。
- ...
- 没有符合条件的配对。
- 输出:
0
示例 2
输入:time = [10, 20, 30]
- 歌曲的余数分别是
[0, 0, 0]。 - 所有三首歌都可以与其它任何一首歌配对,满足
(time[i] + time[j]) % 10 == 0。- (10, 20): 10 + 20 = 30, 能被 10 整除。
- (10, 30): 10 + 30 = 40, 能被 10 整除。
- (20, 30): 20 + 30 = 50, 能被 10 整除。
- 输出:
3
示例 3
输入:time = [7, 3, 5, 5, 10]
- 歌曲的余数分别是
[7, 3, 5, 5, 0]。 - 可能的配对:
- (7, 3): 7 + 3 = 10, 能被 10 整除。
- (5, 5): 5 + 5 = 10, 能被 10 整除。
- 输出:
2
代码实现
def solution(time):
# 计数数组,用于存储每个余数的出现次数
count = [0] * 10
result = 0
# 遍历时间列表,计算每个时间的余数
for t in time:
remainder = t % 10
# 寻找与当前余数配对的余数
complement = (10 - remainder) % 10
result += count[complement]
# 更新当前余数的计数
count[remainder] += 1
return result