线上报警问题分类 | 豆包MarsCode AI刷题

177 阅读5分钟

问题描述

为了避免 AB 实验在迭代过程中导致系统崩溃,系统增加了监控报警功能,通知值班同学对意外情况立刻处理。

在报警的时间范围内,小M同学收到了 𝑁 名用户的反馈,每位用户编号为 1 到 𝑁。小M同学统计了用户命中实验的列表,其中第 𝑖 位用户命中了 𝑘𝑖个实验,第 𝑗 个实验的编号为 𝑎𝑖,𝑗。

小M同学需要对这些问题进行分类。根据先前的经验,小M同学会进行 𝑄 次询问尝试对问题进行分类,第 𝑖 次询问会给出一个序列 𝑏𝑖,1,𝑏𝑖,2,…,𝑏𝑖,𝑐𝑖,𝑐𝑖 表示第 𝑖 次查询的实验数量。当 𝑏𝑖,𝑗>0 时表示命中实验 ∣𝑏𝑖,𝑗∣,否则表示未命中实验 ∣𝑏𝑖,𝑗∣。小M同学需要得到符合这些条件的用户数。例如,序列 1, -2, 3 表示命中实验 1, 3 且未命中实验 2 的用户数量。

输入:

  1. n:表示用户的数量。

  2. m:表示实验的数量。

  3. q:表示查询的次数。

  4. arrayN:一个二维列表,表示每个用户的实验命中情况。

    • arrayN[i] 表示第 i 个用户的实验命中列表。
    • arrayN[i][0]:表示第 i 个用户命中的实验数量。
    • arrayN[i][1:arrayN[i][0]+1]:表示第 i 个用户命中的实验编号。
  5. arrayQ:一个二维列表,表示每次查询的条件。

    • arrayQ[i] 表示第 i 次查询的条件。
    • arrayQ[i][0]:表示第 i 次查询的实验数量。
    • arrayQ[i][1:arrayQ[i][0]+1]:表示第 i 次查询的实验编号。如果实验编号为正数,表示命中该实验;如果为负数,表示未命中该实验。

输出:

一个列表,表示每次查询符合条件的用户数量

问题分析

这个问题的主要难点在题目的理解上,由于输入的信息很多,显得很繁琐,不知道从何入手。

但通过仔细分析,可以发现,最重要的两个输入信息是 arrayQarrayQ

  • arrayN 囊括了用户命中信息。输入中给出的 n,是这个列表的长度,表示用户数量。
  • arrayQ 表明了小M同学查询的相关信息。输入中给出的 q,也就是是这个列表的长度,表示小M同学查询的次数。
  • m 代表实验的数量,可以从 arrayNarrayQ 中所含数字的绝对值的数量多少看出。

针对最重要的两个信息,这题要求的是对于 arrayQ 的每次查询条件,arrayN 中满足条件的用户数量,把这些记录下来形成一个列表输出。

代码书写

本题需要用到集合(set)及相关函数。首先需要把每个用户的实验命中情况转换为集合。

在样例中,可以看出用户可能会命中了多个相同的实验,使用集合可以实现自动去重,确保每个实验编号只被处理一次。

user = [set(arrayN[i][1:arrayN[i][0]+1]) for i in range(n)]
  1. arrayN[i] 表示第 i 个用户的实验命中列表。

  2. arrayN[i][1:arrayN[i][0]+1]

    • arrayN[i] 的第 2 个元素开始,到第 arrayN[i][0] + 1 个元素为止的切片。
    • 由于 arrayN[i][0] 表示第 i 个用户命中的实验数量,这个切片包含了第 i 个用户命中的所有实验编号。
  3. set(arrayN[i][1:arrayN[i][0]+1])

    将上述切片转换为集合(set)。集合会自动去除重复的实验编号,确保每个实验编号只出现一次。

  4. 最后遍历从 0n-1 的所有用户,将其实验命中情况转换为集合,并添加到列表 user 中。

查询实现

对于每一次查询,我们可以用两个集合来表示查询条件,一个为命中实验,另一个储存非命中实验。

arrayQ 中,非命中实验用负数表示,所以我们要在集合中加入它的相反数。

hit= set()
miss = set()
        
for exp in query[1:query[0]+1]:
	if exp > 0:
    	hit.add(exp)
    else:
        miss.add(-exp)

检验每一个用户是否符合查询条件

count = 0
for user_exp in user:
    if hit.issubset(user_exp) and miss.isdisjoint(user_exp):
        count += 1
  • hit.issubset(user_exp)

    • hit 是查询中要求命中的实验集合。
    • user_exp 是当前用户的实验命中集合。
    • issubset 方法用于检查 hit_experiments 是否是 user_exp 的子集。它检查用户是否命中了查询中要求的实验部分。
  • miss.isdisjoint(user_exp)

    • miss 是查询中要求未命中的实验集合。

    • isdisjoint 方法用于检查 miss_experimentsuser_exp 是否不相交。用于判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。在这里,函数检查用户是否未命中要求的实验部分。

完整代码

def solution(n, m, q, arrayN, arrayQ):
    
    user = [set(arrayN[i][1:arrayN[i][0]+1]) for i in range(n)]
    
    result = []
    
    for query in arrayQ:
        # 将查询条件分为命中实验和未命中实验
        hit= set()
        miss = set()
        
        for exp in query[1:query[0]+1]:
            if exp > 0:
                hit.add(exp)
            else:
                miss.add(-exp)
        
        # 统计符合条件的用户数量
        count = 0
        for user_exp in user:
            if hit.issubset(user_exp) and miss.isdisjoint(user_exp):
                count += 1
        
        result.append(count)
    
    return result

if __name__ == "__main__":
    # Add your test cases here
    print(
        solution(
            3,
            3,
            3,
            [[2, 1, 2], [2, 2, 3], [2, 1, 3]],
            [[2, 1, -2], [2, 2, -3], [2, 3, -1]],
        )
        == [1, 1, 1]
    )