题目概述
-
在这个问题中,我们有
N个用户,每个用户可能命中了M个不同的实验。 -
我们会进行
Q次查询,每次查询会指定一些实验,并尝试找出符合命中或未命中条件的用户数量。 -
每个查询中给定的条件有两种:
- 正数实验编号:表示用户必须命中这些实验。
- 负数实验编号:表示用户不能命中这些实验。
例如:
- 查询
{2, 1, -2}表示我们要找出命中了实验1但没有命中实验2的用户数量。
解题步骤
为了实现这个功能,我们将分以下几个步骤来解题:
-
数据存储和初始化:
- 使用一个
HashSet数组来存储每个用户命中的实验编号。 HashSet是一个方便的工具,可以高效地进行集合操作,例如检查一个元素是否存在。
- 使用一个
-
处理每次查询:
- 我们需要遍历每个用户,检查用户是否符合查询条件。
- 每次遍历时,使用查询中的命中/未命中要求,逐一验证用户的实验列表。
-
计数符合查询条件的用户数量:
- 通过验证所有用户,找出符合条件的用户并进行计数,最后将结果返回。
代码解释
java复制代码import java.util.*;
public class Main {
public static List<Integer> solution(int n, int m, int q, int[][] arrayN, int[][] arrayQ) {
// 初始化结果列表
List<Integer> results = new ArrayList<>();
// 将每个用户的实验命中情况存储在 Set 中
HashSet<Integer>[] userExperiments = new HashSet[n]; // 使用 HashSet 来存储每个用户命中的实验
for (int i = 0; i < n; i++) {
userExperiments[i] = new HashSet<>();
for (int j = 1; j <= arrayN[i][0]; j++) {
userExperiments[i].add(arrayN[i][j]);
}
}
// 处理每次查询
for (int i = 0; i < q; i++) {
int count = 0;
// 遍历所有用户,检查是否符合查询条件
for (int user = 0; user < n; user++) {
boolean matches = true;
for (int j = 1; j <= arrayQ[i][0]; j++) {
int experiment = arrayQ[i][j];
if (experiment > 0) {
// 检查是否命中实验,如果用户没有命中这个实验则不匹配
if (!userExperiments[user].contains(experiment)) {
matches = false;
break;
}
} else {
// 检查未命中实验,如果用户命中了这个实验则不匹配
if (userExperiments[user].contains(-experiment)) {
matches = false;
break;
}
}
}
// 如果当前用户符合所有查询条件,计数加1
if (matches) {
count++;
}
}
// 将符合条件的用户数量添加到结果列表中
results.add(count);
}
return results;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(3, 3, 3, new int[][] { { 2, 1, 2 }, { 2, 2, 3 }, { 2, 1, 3 } },
new int[][] { { 2, 1, -2 }, { 2, 2, -3 }, { 2, 3, -1 } }).equals(Arrays.asList(1, 1, 1)));
}
}
代码解析
-
初始化用户实验命中情况:
java复制代码HashSet<Integer>[] userExperiments = new HashSet[n]; // 使用 HashSet 来存储每个用户命中的实验 for (int i = 0; i < n; i++) { userExperiments[i] = new HashSet<>(); for (int j = 1; j <= arrayN[i][0]; j++) { userExperiments[i].add(arrayN[i][j]); } }- 目的:用
HashSet存储每个用户命中的实验编号,便于快速查找。 - 实现细节:遍历
arrayN来初始化用户的实验命中情况。 arrayN[i][0]表示第i个用户命中的实验数量,然后将每个实验编号添加到HashSet中。
- 目的:用
-
处理查询:
java复制代码for (int i = 0; i < q; i++) { int count = 0; // 遍历所有用户,检查是否符合查询条件 for (int user = 0; user < n; user++) { boolean matches = true; for (int j = 1; j <= arrayQ[i][0]; j++) { int experiment = arrayQ[i][j]; if (experiment > 0) { // 检查是否命中实验,如果用户没有命中这个实验则不匹配 if (!userExperiments[user].contains(experiment)) { matches = false; break; } } else { // 检查未命中实验,如果用户命中了这个实验则不匹配 if (userExperiments[user].contains(-experiment)) { matches = false; break; } } } // 如果当前用户符合所有查询条件,计数加1 if (matches) { count++; } } // 将符合条件的用户数量添加到结果列表中 results.add(count); }-
遍历每个用户:对每个用户检查他们是否符合当前的查询条件。
-
验证条件
:
- 对于查询条件中的每个实验编号,正数表示必须命中,负数表示必须未命中。
- 利用
HashSet的快速查找特性来检查条件。
-
匹配用户数量:将符合条件的用户数量存入结果列表。
-
-
输出结果:
- 在
main函数中,我们通过测试用例来验证解决方案的正确性。
- 在
代码运行分析
-
时间复杂度:
- 用户实验命中情况的初始化:
O(N * M),其中M是每个用户的实验数量。 - 每次查询遍历所有用户:
O(Q * N * C),其中C是每次查询中涉及的实验数量。 - 因此,时间复杂度为
O(N * M + Q * N * C)。
- 用户实验命中情况的初始化:
-
空间复杂度:
- 使用了
O(N)个HashSet,每个HashSet存储一个用户命中的实验编号。
- 使用了
示例分析
输入:
-
用户实验数据:
yaml复制代码用户 1: 命中实验 1, 2 用户 2: 命中实验 2, 3 用户 3: 命中实验 1, 3 -
查询数据:
yaml复制代码查询 1: 命中实验 1, 未命中实验 2 (结果为 1) 查询 2: 命中实验 2, 未命中实验 3 (结果为 1) 查询 3: 命中实验 3, 未命中实验 1 (结果为 1)
总结
- 这道题的核心是通过
HashSet来快速存储和查找用户命中的实验,并通过循环遍历来验证查询条件。 - 正确地处理命中和未命中条件是这道题的关键所在。
- 利用集合结构可以有效地加快匹配的速度,从而提高查询效率。