People Whose List of Favorite Companies Is Not a Subset of Another List
MedPrompt
Given the array favoriteCompanies where favoriteCompanies[i] is the list of favorites companies for the ith person (indexed from 0).
Return the indices of people whose list of favorite companies is not a subset of any other list of favorites companies. You must return the indices in increasing order.
Example 1:
Input: favoriteCompanies = [["leetcode","google","facebook"],["google","microsoft"],["google","facebook"],["google"],["amazon"]]
Output: [0,1,4]
Explanation:
Person with index=2 has favoriteCompanies[2]=["google","facebook"] which is a subset of favoriteCompanies[0]=["leetcode","google","facebook"] corresponding to the person with index 0.
Person with index=3 has favoriteCompanies[3]=["google"] which is a subset of favoriteCompanies[0]=["leetcode","google","facebook"] and favoriteCompanies[1]=["google","microsoft"].
Other lists of favorite companies are not a subset of another list, therefore, the answer is [0,1,4].Example 2:
Input: favoriteCompanies = [["leetcode","google","facebook"],["leetcode","amazon"],["facebook","google"]]
Output: [0,1]
Explanation: In this case favoriteCompanies[2]=["facebook","google"] is a subset of favoriteCompanies[0]=["leetcode","google","facebook"], therefore, the answer is [0,1].Example 3:
Input: favoriteCompanies = [["leetcode"],["google"],["facebook"],["amazon"]]
Output: [0,1,2,3]
Constraints:
1 <= favoriteCompanies.length <= 1001 <= favoriteCompanies[i].length <= 5001 <= favoriteCompanies[i][j].length <= 20- All strings in
favoriteCompanies[i]are distinct. - All lists of favorite companies are distinct, that is, If we sort alphabetically each list then
favoriteCompanies[i] != favoriteCompanies[j]. - All strings consist of lowercase English letters only.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem statement into code. We iterate through every pair of people (i, j) and check if the favorite company list of person i is a subset of person j's list. To make the subset check efficient, we convert the lists of companies into Hash Sets, which provide average O(1) time for containment checks.
Algorithm
- Initialize an empty list
resultto store the final indices. - Convert each person's list of favorite companies from a
List<String>to aHashSet<String>. This allows for faster lookups when checking for subsets. Store these sets in a new list, saycompanySets. - Iterate through each person
ifrom0ton-1(wherenis the total number of people). - For each person
i, assume their list is not a subset of any other. Use a boolean flag,isSubset, initialized tofalse. - Start a nested loop to compare person
iwith every other personj. - If
iandjare the same, skip the comparison. - A crucial optimization: if the size of
i's company set is greater than or equal to the size ofj's set,i's list cannot be a proper subset ofj's list. Skip the detailed check in this case. - Perform the subset check:
companySets.get(j).containsAll(companySets.get(i)). This checks if every company ini's set is present inj's set. - If
i's set is a subset ofj's set, setisSubset = trueand break out of the inner loop (overj), as we've confirmed personi's list is a subset. - After the inner loop completes, if
isSubsetis stillfalse, it means no other list contains all of personi's favorite companies. Add the indexito theresultlist. - After iterating through all people, return the
resultlist.
Walkthrough
The core idea is to perform a pairwise comparison among all the lists of favorite companies. For each person's list, we check if it's a subset of any other person's list.
To implement the subset check efficiently, we first convert each List<String> of companies into a HashSet<String>. This data structure is ideal for checking the presence of an element quickly.
The algorithm proceeds as follows:
- Create a list of
HashSet<String>from the inputfavoriteCompanies. - Iterate through each person
i. - For each
i, iterate through all other peoplej. - If
list[i]is a subset oflist[j], we markias a subset and move to the next person. - If we finish checking all
jfor a giveniand haven't found it to be a subset of any other list, we addito our result.
import java.util.*; class Solution { public List<Integer> peopleIndexes(List<List<String>> favoriteCompanies) { int n = favoriteCompanies.size(); List<Set<String>> companySets = new ArrayList<>(); for (List<String> companies : favoriteCompanies) { companySets.add(new HashSet<>(companies)); } List<Integer> result = new ArrayList<>(); for (int i = 0; i < n; i++) { boolean isSubset = false; for (int j = 0; j < n; j++) { if (i == j) { continue; } // A list can only be a proper subset of a larger list. if (companySets.get(i).size() >= companySets.get(j).size()) { continue; } // Check if set i is a subset of set j if (companySets.get(j).containsAll(companySets.get(i))) { isSubset = true; break; } } if (!isSubset) { result.add(i); } } return result; }}Complexity
Time
O(N^2 * M * L), where N is the number of people, M is the maximum number of companies, and L is the maximum length of a company name. The N^2 factor comes from the nested loops. The M*L factor comes from the `containsAll` operation on sets of strings, which involves hashing and comparing M strings of average length L.
Space
O(N * M * L), where N is the number of people, M is the maximum number of companies for a person, and L is the maximum length of a company name. This space is required to store the hash sets of company names.
Trade-offs
Pros
Relatively simple to understand and implement.
Directly follows the logic of the problem description.
Cons
The time complexity is high due to repeated string hashing and comparisons within the nested loops.
It may result in a 'Time Limit Exceeded' (TLE) error on platforms with strict time limits, especially for larger inputs.
The bottleneck in the brute-force approach is the repeated string operations (hashing and comparison) inside the main loops. This optimized approach tackles that by first mapping every unique company string to a unique integer. All subsequent comparisons and subset checks are then performed on sets of integers, which is significantly faster than operating on strings.
Algorithm
- Map Strings to Integers:
- Create a
HashMap<String, Integer>to map each unique company name to a unique integer ID. - Iterate through all companies for all people. If a company name is not in the map, assign it a new integer ID and add it to the map.
- Create a
- Create Integer Sets:
- Create a
List<Set<Integer>>. - For each person, create a
HashSet<Integer>representing their favorite companies using the integer IDs from the map.
- Create a
- Perform Subset Check:
- Initialize an empty list
result. - Iterate through each person
ifrom0ton-1. - Assume
iis not a subset (isSubset = false). - Iterate through every other person
j. - If the size of
i's integer set is greater than or equal to the size ofj's set,icannot be a proper subset, so continue. - Check if
j's integer set contains all elements ofi's integer set usingsetJ.containsAll(setI). - If it is a subset, set
isSubset = trueand break the inner loop.
- Initialize an empty list
- Collect Result:
- If
isSubsetremainsfalseafter checking allj, addito theresultlist. - Return the
resultlist.
- If
Walkthrough
This method improves upon the brute-force approach by reducing the cost of comparisons inside the nested loops.
-
Integer Mapping: We first perform a pre-processing step. We iterate through all the companies of all people and build a map from each unique company name (a
String) to a uniqueInteger. This ensures that each company is represented by a simple, fast-to-compare integer. -
Integer Set Creation: We then create a new list of sets, where each set contains the integer IDs of a person's favorite companies.
-
Subset Check: With the integer sets, we perform the same pairwise comparison as in the brute-force method. However, the
containsAlloperation on sets of integers is much faster than on sets of strings, as integer hashing and comparison is a constant time operation.
This pre-computation work pays off by speeding up the most expensive part of the algorithm—the N*N comparisons.
import java.util.*; class Solution { public List<Integer> peopleIndexes(List<List<String>> favoriteCompanies) { // 1. Map strings to integers and create integer sets Map<String, Integer> companyToInt = new HashMap<>(); int nextId = 0; List<Set<Integer>> companyIntSets = new ArrayList<>(); for (List<String> companies : favoriteCompanies) { Set<Integer> currentSet = new HashSet<>(); // Sort companies to potentially improve cache performance, though not strictly necessary for correctness // Collections.sort(companies); for (String company : companies) { if (!companyToInt.containsKey(company)) { companyToInt.put(company, nextId++); } currentSet.add(companyToInt.get(company)); } companyIntSets.add(currentSet); } // 2. Perform subset check on integer sets int n = favoriteCompanies.size(); List<Integer> result = new ArrayList<>(); for (int i = 0; i < n; i++) { boolean isSubset = false; for (int j = 0; j < n; j++) { if (i == j) { continue; } Set<Integer> setI = companyIntSets.get(i); Set<Integer> setJ = companyIntSets.get(j); if (setI.size() >= setJ.size()) { continue; } if (setJ.containsAll(setI)) { isSubset = true; break; } } if (!isSubset) { result.add(i); } } return result; }}Complexity
Time
O(N*M*L + N^2*M). The first term `N*M*L` is for the initial mapping of strings to integers. The second term `N^2*M` is for the nested loops performing subset checks on integer sets. This is a major improvement over the O(N^2*M*L) of the brute-force approach.
Space
O(N * M * L). The `companyToInt` map can store up to `N * M` unique companies, with the total space for strings being dominant. The integer sets require O(N * M) space.
Trade-offs
Pros
Significantly more efficient and likely to pass within time limits.
Reduces the complexity of the core comparison loop by replacing expensive string operations with cheap integer operations.
Cons
Requires an initial pre-processing step to build the string-to-integer map.
Uses more memory to store both the integer map and the integer sets.
Solutions
Solution
class Solution { public List < Integer > peopleIndexes ( List < List < String >> favoriteCompanies ) { Map < String , Integer > d = new HashMap <>(); int idx = 0 ; int n = favoriteCompanies . size (); Set < Integer >[] t = new Set [ n ]; for ( int i = 0 ; i < n ; ++ i ) { var v = favoriteCompanies . get ( i ); for ( var c : v ) { if (! d . containsKey ( c )) { d . put ( c , idx ++); } } Set < Integer > s = new HashSet <>(); for ( var c : v ) { s . add ( d . get ( c )); } t [ i ] = s ; } List < Integer > ans = new ArrayList <>(); for ( int i = 0 ; i < n ; ++ i ) { boolean ok = true ; for ( int j = 0 ; j < n ; ++ j ) { if ( i != j ) { if ( t [ j ]. containsAll ( t [ i ])) { ok = false ; break ; } } } if ( ok ) { ans . add ( i ); } } return ans ; } }class Solution {public: vector<int> peopleIndexes(vector<vector<string>> &favoriteCompanies) { unordered_map<string, int> d; int idx = 0, n = favoriteCompanies.size(); vector<unordered_set<int>> t(n); for (int i = 0; i < n; ++i) { auto v = favoriteCompanies[i]; for (auto &c : v) { if (!d.count(c)) { d[c] = idx++; } } unordered_set<int> s; for (auto &c : v) { s.insert(d[c]); } t[i] = s; } vector<int> ans; for (int i = 0; i < n; ++i) { bool ok = true; for (int j = 0; j < n; ++j) { if (i == j) continue; if (check(t[i], t[j])) { ok = false; break; } } if (ok) { ans.push_back(i); } } return ans; } bool check(unordered_set<int> &nums1, unordered_set<int> &nums2) { for (int v : nums1) { if (!nums2.count(v)) { return false; } } return true; }};class Solution: def peopleIndexes(self, favoriteCompanies: List[List[str]]) -> List[int]: d = {} idx = 0 t = [] for v in favoriteCompanies: for c in v: if c not in d: d[c] = idx idx += 1 t . append({d[c] for c in v}) ans = [] for i, nums1 in enumerate(t): ok = True for j, nums2 in enumerate(t): if i == j: continue if not (nums1 - nums2): ok = False break if ok: ans . append(i) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.