1452. People Whose List of Favorite Companies Is Not a Subset of Another List
Problem Description
You are given an array favoriteCompanies
where favoriteCompanies[i]
contains the list of favorite companies for the i-th
person (0-indexed).
Your task is to find all people whose list of favorite companies is not a subset of any other person's list of favorite companies. Return the indices of these people in increasing order.
In other words, for each person, check if their favorite companies list is completely contained within someone else's favorite companies list. If a person's favorites are not fully contained in anyone else's list, include their index in the result.
For example:
- If person 0 has favorites
["leetcode", "google"]
- And person 1 has favorites
["leetcode", "google", "facebook"]
- Then person 0's favorites are a subset of person 1's favorites (all companies in person 0's list appear in person 1's list)
- So person 0 would not be included in the answer, but person 1 might be (depending on other people's lists)
The solution approach converts company names to unique integers for efficient comparison. It creates sets of integers for each person's favorite companies, then checks if any person's set is a subset of another person's set using set operations. The condition (nums[i] & nums[j]) == nums[i]
checks if nums[i]
is a subset of nums[j]
by verifying if their intersection equals nums[i]
.
Intuition
The key insight is recognizing that checking if one list is a subset of another is fundamentally a set operation problem. When we need to determine if all elements of list A exist in list B, we're essentially asking: is A a subset of B?
Initially, we might think to compare the string lists directly, but string comparisons are expensive, especially when done repeatedly. Each string comparison takes time proportional to the string length, and we'd need to do this for every company name when checking subsets.
This leads us to a clever optimization: what if we could convert these strings to numbers? Numbers can be compared in constant time, making our subset checks much faster. We can create a mapping where each unique company name gets assigned a unique integer ID. For example, "google" might become 0, "facebook" becomes 1, and so on.
Once we have this mapping, each person's favorite companies can be represented as a set of integers instead of a set of strings. Now the subset check becomes elegant: for person i
to be a subset of person j
, every number in person i
's set must also be in person j
's set.
The mathematical property we exploit is that if set A is a subset of set B, then the intersection of A and B equals A itself. In code terms: A ∩ B = A
means A is a subset of B. This gives us an efficient way to check the subset relationship using the expression (nums[i] & nums[j]) == nums[i]
.
Finally, for each person, we check against all other people. If we find even one other person whose favorites contain all of our current person's favorites, then the current person's list is a subset and shouldn't be included in the answer. Only those whose lists are not subsets of any other list make it to the final result.
Solution Approach
The implementation follows a three-phase approach: mapping, conversion, and subset checking.
Phase 1: String to Integer Mapping
We start by creating a dictionary d
to map each unique company name to a unique integer ID. We also initialize:
idx = 0
as a counter for assigning unique IDsn = len(favoriteCompanies)
to store the number of peoplenums
as a list ofn
empty sets to store integer representations of each person's favorites
idx = 0
d = {}
n = len(favoriteCompanies)
nums = [set() for _ in range(n)]
Phase 2: Convert Company Names to Integer Sets
We iterate through each person's favorite companies. For each company name:
- If it's not in our dictionary, we assign it a new unique ID and increment
idx
- We add the company's integer ID to the corresponding person's set in
nums
for i, ss in enumerate(favoriteCompanies):
for s in ss:
if s not in d:
d[s] = idx
idx += 1
nums[i].add(d[s])
After this phase, each person's favorite companies are represented as a set of integers. For example, if person 0 likes ["google", "leetcode"]
and these map to {0, 1}
, then nums[0] = {0, 1}
.
Phase 3: Subset Checking
For each person i
, we check if their set is a subset of any other person j
's set:
ans = []
for i in range(n):
if not any(i != j and (nums[i] & nums[j]) == nums[i] for j in range(n)):
ans.append(i)
The key expression (nums[i] & nums[j]) == nums[i]
checks if nums[i]
is a subset of nums[j]
:
nums[i] & nums[j]
computes the intersection (common elements)- If this intersection equals
nums[i]
, it means all elements ofnums[i]
are innums[j]
- The condition
i != j
ensures we don't compare a person with themselves
The any()
function returns True
if at least one person j
has nums[i]
as a subset. We use not any()
to find people whose favorites are NOT a subset of anyone else's.
The time complexity is O(n² × m)
where n
is the number of people and m
is the average size of favorite company lists, as we need to check each pair of people and perform set operations.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Input:
favoriteCompanies = [ ["leetcode", "google", "facebook"], ["google", "microsoft"], ["google", "facebook"], ["google"], ["amazon"] ]
Phase 1: Create String to Integer Mapping
We build a dictionary to map company names to unique integers:
- "leetcode" → 0
- "google" → 1
- "facebook" → 2
- "microsoft" → 3
- "amazon" → 4
Phase 2: Convert to Integer Sets
Transform each person's favorites into sets of integers:
- Person 0: ["leetcode", "google", "facebook"] → {0, 1, 2}
- Person 1: ["google", "microsoft"] → {1, 3}
- Person 2: ["google", "facebook"] → {1, 2}
- Person 3: ["google"] → {1}
- Person 4: ["amazon"] → {4}
Phase 3: Check for Subsets
Now we check if each person's set is a subset of any other person's set:
Person 0 with set {0, 1, 2}:
- vs Person 1: {0, 1, 2} ∩ {1, 3} = {1} ≠ {0, 1, 2} → Not a subset
- vs Person 2: {0, 1, 2} ∩ {1, 2} = {1, 2} ≠ {0, 1, 2} → Not a subset
- vs Person 3: {0, 1, 2} ∩ {1} = {1} ≠ {0, 1, 2} → Not a subset
- vs Person 4: {0, 1, 2} ∩ {4} = {} ≠ {0, 1, 2} → Not a subset
- Result: Person 0 is not a subset of anyone else ✓
Person 1 with set {1, 3}:
- vs Person 0: {1, 3} ∩ {0, 1, 2} = {1} ≠ {1, 3} → Not a subset
- vs Person 2: {1, 3} ∩ {1, 2} = {1} ≠ {1, 3} → Not a subset
- vs Person 3: {1, 3} ∩ {1} = {1} ≠ {1, 3} → Not a subset
- vs Person 4: {1, 3} ∩ {4} = {} ≠ {1, 3} → Not a subset
- Result: Person 1 is not a subset of anyone else ✓
Person 2 with set {1, 2}:
- vs Person 0: {1, 2} ∩ {0, 1, 2} = {1, 2} = {1, 2} → Is a subset!
- Result: Person 2 is a subset of Person 0 ✗
Person 3 with set {1}:
- vs Person 0: {1} ∩ {0, 1, 2} = {1} = {1} → Is a subset!
- Result: Person 3 is a subset of Person 0 ✗
Person 4 with set {4}:
- vs Person 0: {4} ∩ {0, 1, 2} = {} ≠ {4} → Not a subset
- vs Person 1: {4} ∩ {1, 3} = {} ≠ {4} → Not a subset
- vs Person 2: {4} ∩ {1, 2} = {} ≠ {4} → Not a subset
- vs Person 3: {4} ∩ {1} = {} ≠ {4} → Not a subset
- Result: Person 4 is not a subset of anyone else ✓
Final Answer: [0, 1, 4]
The people at indices 0, 1, and 4 have favorite company lists that are not subsets of any other person's list.
Solution Implementation
1class Solution:
2 def peopleIndexes(self, favoriteCompanies: List[List[str]]) -> List[int]:
3 # Map company names to unique integer IDs for efficient set operations
4 company_to_id = {}
5 next_id = 0
6
7 # Number of people
8 num_people = len(favoriteCompanies)
9
10 # Convert each person's company list to a set of company IDs
11 company_id_sets = [set() for _ in range(num_people)]
12
13 # Build the mapping and convert company names to IDs
14 for person_idx, companies in enumerate(favoriteCompanies):
15 for company in companies:
16 # Assign a unique ID to each company if not already assigned
17 if company not in company_to_id:
18 company_to_id[company] = next_id
19 next_id += 1
20 # Add the company ID to this person's set
21 company_id_sets[person_idx].add(company_to_id[company])
22
23 # Find people whose favorite companies are not a subset of others
24 result = []
25 for i in range(num_people):
26 # Check if person i's companies are a subset of any other person's companies
27 is_subset_of_another = False
28 for j in range(num_people):
29 # Skip comparing with self
30 if i == j:
31 continue
32 # Check if set i is a subset of set j
33 if company_id_sets[i].issubset(company_id_sets[j]):
34 is_subset_of_another = True
35 break
36
37 # If not a subset of any other person's list, add to result
38 if not is_subset_of_another:
39 result.append(i)
40
41 return result
42
1class Solution {
2 public List<Integer> peopleIndexes(List<List<String>> favoriteCompanies) {
3 int numberOfPeople = favoriteCompanies.size();
4
5 // Map to store company name to unique ID mapping
6 Map<String, Integer> companyToIdMap = new HashMap<>();
7 int nextCompanyId = 0;
8
9 // Array of sets to store company IDs for each person
10 Set<Integer>[] personCompanyIds = new Set[numberOfPeople];
11 Arrays.setAll(personCompanyIds, i -> new HashSet<>());
12
13 // Convert company names to IDs for efficient comparison
14 for (int personIndex = 0; personIndex < numberOfPeople; personIndex++) {
15 List<String> companies = favoriteCompanies.get(personIndex);
16
17 for (String company : companies) {
18 // Assign unique ID to each company if not already present
19 if (!companyToIdMap.containsKey(company)) {
20 companyToIdMap.put(company, nextCompanyId++);
21 }
22 // Add company ID to person's set
23 personCompanyIds[personIndex].add(companyToIdMap.get(company));
24 }
25 }
26
27 List<Integer> result = new ArrayList<>();
28
29 // Check if each person's companies are a subset of any other person's companies
30 for (int currentPerson = 0; currentPerson < numberOfPeople; currentPerson++) {
31 boolean isNotSubset = true;
32
33 for (int otherPerson = 0; otherPerson < numberOfPeople && isNotSubset; otherPerson++) {
34 // Skip comparing person with themselves
35 if (currentPerson != otherPerson) {
36 // Check if current person's companies are subset of other person's companies
37 if (personCompanyIds[otherPerson].containsAll(personCompanyIds[currentPerson])) {
38 isNotSubset = false;
39 }
40 }
41 }
42
43 // Add to result if not a subset of any other person's companies
44 if (isNotSubset) {
45 result.add(currentPerson);
46 }
47 }
48
49 return result;
50 }
51}
52
1class Solution {
2public:
3 vector<int> peopleIndexes(vector<vector<string>>& favoriteCompanies) {
4 int numPeople = favoriteCompanies.size();
5
6 // Map each company name to a unique integer ID for efficient comparison
7 unordered_map<string, int> companyToId;
8 int nextId = 0;
9
10 // Store each person's favorite companies as a set of integer IDs
11 vector<unordered_set<int>> personCompanyIds(numPeople);
12
13 // Convert company names to IDs for each person
14 for (int i = 0; i < numPeople; ++i) {
15 for (const auto& company : favoriteCompanies[i]) {
16 // Assign new ID if company hasn't been seen before
17 if (!companyToId.contains(company)) {
18 companyToId[company] = nextId++;
19 }
20 // Add company ID to this person's set
21 personCompanyIds[i].insert(companyToId[company]);
22 }
23 }
24
25 // Lambda function to check if set 'subset' is a subset of set 'superset'
26 auto isSubset = [](const unordered_set<int>& subset, const unordered_set<int>& superset) {
27 // Check if every element in subset exists in superset
28 for (int element : subset) {
29 if (!superset.contains(element)) {
30 return false;
31 }
32 }
33 return true;
34 };
35
36 vector<int> result;
37
38 // Check each person to see if their companies are a subset of another person's
39 for (int i = 0; i < numPeople; ++i) {
40 bool isNotSubset = true;
41
42 // Compare with all other people
43 for (int j = 0; j < numPeople && isNotSubset; ++j) {
44 // Skip self-comparison and check if person i's companies are a subset of person j's
45 if (i != j && isSubset(personCompanyIds[i], personCompanyIds[j])) {
46 isNotSubset = false;
47 }
48 }
49
50 // Add to result if this person's companies are not a subset of any other person's
51 if (isNotSubset) {
52 result.push_back(i);
53 }
54 }
55
56 return result;
57 }
58};
59
1/**
2 * Finds indices of people whose favorite companies are not a subset of any other person's favorites
3 * @param favoriteCompanies - Array where each element is a list of favorite companies for person i
4 * @returns Array of indices of people whose favorites are not subsets of others
5 */
6function peopleIndexes(favoriteCompanies: string[][]): number[] {
7 const peopleCount = favoriteCompanies.length;
8
9 // Map to store company name to unique ID mapping
10 const companyToId: Map<string, number> = new Map();
11 let nextCompanyId = 0;
12
13 // Array of sets to store company IDs for each person
14 const personCompanyIds: Set<number>[] = Array.from(
15 { length: peopleCount },
16 () => new Set<number>()
17 );
18
19 // Convert company names to IDs for efficient comparison
20 for (let personIndex = 0; personIndex < peopleCount; personIndex++) {
21 for (const companyName of favoriteCompanies[personIndex]) {
22 // Assign new ID if company hasn't been seen before
23 if (!companyToId.has(companyName)) {
24 companyToId.set(companyName, nextCompanyId++);
25 }
26 // Add company ID to person's set
27 personCompanyIds[personIndex].add(companyToId.get(companyName)!);
28 }
29 }
30
31 /**
32 * Checks if set 'subset' is a subset of set 'superset'
33 * @param subset - Set to check if it's a subset
34 * @param superset - Set to check against
35 * @returns true if subset is contained in superset, false otherwise
36 */
37 const isSubset = (subset: Set<number>, superset: Set<number>): boolean => {
38 for (const element of subset) {
39 if (!superset.has(element)) {
40 return false;
41 }
42 }
43 return true;
44 };
45
46 const result: number[] = [];
47
48 // Check each person's companies against all others
49 for (let currentPerson = 0; currentPerson < peopleCount; currentPerson++) {
50 let isNotSubset = true;
51
52 // Compare with every other person
53 for (let otherPerson = 0; otherPerson < peopleCount && isNotSubset; otherPerson++) {
54 // Skip self-comparison and check if current is subset of other
55 if (currentPerson !== otherPerson &&
56 isSubset(personCompanyIds[currentPerson], personCompanyIds[otherPerson])) {
57 isNotSubset = false;
58 }
59 }
60
61 // Add to result if not a subset of any other person's companies
62 if (isNotSubset) {
63 result.push(currentPerson);
64 }
65 }
66
67 return result;
68}
69
Time and Space Complexity
Time Complexity: O(n × m × k + n² × m)
The time complexity consists of two main parts:
- Building the mapping dictionary and converting strings to numbers:
O(n × m × k)
, where we iterate throughn
people, each with an average ofm
favorite companies, and each company string has an average length ofk
characters (for dictionary operations). - Checking if each person's set is a subset of any other person's set:
O(n² × m)
, where for each of then
people, we check against all othern-1
people, and each subset check(nums[i] & nums[j]) == nums[i]
takesO(m)
time in the worst case.
Space Complexity: O(n × m)
The space complexity comes from:
- Dictionary
d
storing all unique company strings:O(n × m)
in the worst case when all companies are unique - List
nums
containingn
sets, where each set contains at mostm
company indices:O(n × m)
- Output list
ans
:O(n)
in the worst case
The dominant space factor is O(n × m)
.
Here, n
is the number of people (length of favoriteCompanies
), m
is the average number of companies per person, and k
is the average length of each company name string.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Subset Check Logic
A common mistake is checking if nums[j]
is a subset of nums[i]
instead of checking if nums[i]
is a subset of nums[j]
. This reversal would give completely wrong results.
Incorrect:
# Wrong: This checks if j is a subset of i if (nums[j] & nums[i]) == nums[j]: is_subset_of_another = True
Correct:
# Right: This checks if i is a subset of j if (nums[i] & nums[j]) == nums[i]: is_subset_of_another = True # Or more clearly: if nums[i].issubset(nums[j]): is_subset_of_another = True
2. String Comparison Instead of Set Operations
Attempting to directly compare lists of strings without converting to sets leads to incorrect results, as list comparison checks order and exact equality, not subset relationships.
Incorrect:
# Wrong: Lists don't have subset operations
for i in range(n):
is_subset = False
for j in range(n):
if i != j and all(company in favoriteCompanies[j] for company in favoriteCompanies[i]):
is_subset = True
break
This approach has O(n² × m²) complexity due to nested string searches and may fail with duplicate companies in lists.
Correct:
# Convert to sets first for O(1) membership checks
company_sets = [set(companies) for companies in favoriteCompanies]
for i in range(n):
if not any(i != j and company_sets[i].issubset(company_sets[j]) for j in range(n)):
result.append(i)
3. Forgetting Self-Comparison Check
Omitting the i != j
condition causes every person's set to be considered a subset of itself, resulting in an empty answer list.
Incorrect:
# Wrong: Every set is a subset of itself
for i in range(n):
if not any((nums[i] & nums[j]) == nums[i] for j in range(n)):
ans.append(i)
# This will always return an empty list
Correct:
# Must exclude self-comparison
for i in range(n):
if not any(i != j and (nums[i] & nums[j]) == nums[i] for j in range(n)):
ans.append(i)
4. Unnecessary String-to-Integer Mapping
While the original solution maps strings to integers, this step is actually unnecessary since Python's set operations work efficiently with strings. The mapping adds complexity without significant performance benefit for this problem.
Overcomplicated:
# Unnecessary mapping phase
company_to_id = {}
next_id = 0
company_id_sets = [set() for _ in range(num_people)]
for person_idx, companies in enumerate(favoriteCompanies):
for company in companies:
if company not in company_to_id:
company_to_id[company] = next_id
next_id += 1
company_id_sets[person_idx].add(company_to_id[company])
Simplified:
# Direct set conversion is cleaner and equally efficient
company_sets = [set(companies) for companies in favoriteCompanies]
result = []
for i in range(len(company_sets)):
if not any(i != j and company_sets[i].issubset(company_sets[j])
for j in range(len(company_sets))):
result.append(i)
The simplified version maintains O(n² × m) complexity while being more readable and less error-prone.
Which data structure is used in a depth first search?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!