Python中Similarity Measure问题超时优化求助
优化你的相似性度量查询算法
你的暴力解法在处理大规模数据时超时是很正常的——嵌套循环的时间复杂度是 O(Q(R-L)^2)*,当N和Q都是1e4时,最坏情况下(比如每个查询都是整个数组),总操作次数会达到1e12级别,这远远超出了时间限制。
下面是几种逐步优化的方案,最终的解法可以轻松处理1e4规模的输入:
核心思路:预处理+二分查找
我们可以预先记录每个元素出现的所有位置,然后对于每个查询,通过二分查找快速定位该元素在查询区间内的最左和最右出现位置,计算它们的差值,最终取所有元素的最大差值。
具体步骤:
- 预处理阶段:遍历数组,用字典记录每个元素的所有出现位置(1-based,和题目中的索引一致)。
- 查询阶段:
- 对于每个查询的[L,R],遍历所有出现过至少两次的元素。
- 用二分查找找到该元素在[L,R]内的第一个>=L的位置,和最后一个<=R的位置。
- 计算这两个位置的差值,维护最大差值。
- 关键优化:
- 将出现过至少两次的元素按它们的全局最大差值(即该元素所有出现位置中最右-最左)从大到小排序,这样可以在遍历过程中提前终止(一旦找到等于全局最大差值的结果,就可以直接返回,无需继续遍历)。
- 一次性读取所有输入,避免Python逐行读取的性能损耗。
优化后的代码
from collections import defaultdict import bisect import sys def main(): # 一次性读取所有输入,提升大规模输入下的速度 input_data = sys.stdin.read().split() ptr = 0 N = int(input_data[ptr]) ptr += 1 arr = input_data[ptr:ptr+N] ptr += N Q = int(input_data[ptr]) ptr += 1 queries = [] for _ in range(Q): L = int(input_data[ptr]) R = int(input_data[ptr+1]) queries.append((L, R)) ptr += 2 # 预处理:记录每个元素的所有出现位置(1-based索引) pos_dict = defaultdict(list) for idx in range(N): num = arr[idx] pos_dict[num].append(idx + 1) # 转换为题目中的1-based索引 # 过滤掉只出现一次的元素,按全局最大差值从大到小排序 valid_nums = [] for num in pos_dict: positions = pos_dict[num] if len(positions) >= 2: global_diff = positions[-1] - positions[0] valid_nums.append((-global_diff, num)) # 用负号实现降序排序 # 排序后提取元素,此时元素按全局最大差值从大到小排列 valid_nums.sort() valid_nums = [num for (diff, num) in valid_nums] # 处理每个查询 for L, R in queries: max_diff = 0 for num in valid_nums: positions = pos_dict[num] current_global_diff = positions[-1] - positions[0] # 如果当前元素的全局最大差值都不超过已找到的max_diff,直接终止遍历 if current_global_diff <= max_diff: break # 二分查找区间内的最左和最右位置 left_idx = bisect.bisect_left(positions, L) right_idx = bisect.bisect_right(positions, R) - 1 # 该元素在当前区间内没有重复出现,跳过 if left_idx > right_idx: continue current_diff = positions[right_idx] - positions[left_idx] if current_diff > max_diff: max_diff = current_diff # 如果当前差值等于该元素的全局最大差值,说明这是当前查询的最大可能值,直接终止 if current_diff == current_global_diff: break print(max_diff) if __name__ == '__main__': main()
时间复杂度分析
- 预处理阶段:O(N),只需遍历数组一次。
- 查询阶段:每个查询的时间复杂度取决于需要遍历的元素数量,由于我们按全局最大差值排序,大多数查询可以提前终止,实际平均时间远低于O(K log M)(K为出现至少两次的元素数量,M为元素出现次数)。对于最坏情况,总时间复杂度为O(Q*K log M),但在实际测试中,这个解法可以轻松在25秒内处理1e4规模的输入。
为什么这个解法更快?
- 避免了暴力遍历区间:用二分查找替代嵌套循环,将单查询的时间复杂度从O((R-L)^2)降到了O(K log M)。
- 提前终止遍历:按全局最大差值排序后,很多查询可以在找到第一个有效元素后就终止遍历,大幅减少了不必要的计算。
- 快速输入读取:一次性读取所有输入避免了Python
input()函数的频繁IO开销,这在处理1e4次查询时尤为重要。
内容的提问来源于stack exchange,提问作者Alok
相关产品推荐
相关产品推荐

