There are n people and 40 types of hats labeled from 1 to 40.
Given a 2D integer array hats, where hats[i] is a list of all hats preferred by the ith person.
Return the number of ways that n people can wear different hats from each other.
Since the answer may be too large, return it modulo 109 + 7.
Example 1:
Input: hats = [[3,4],[4,5],[5]] Output: 1 Explanation: There is only one way to choose hats given the conditions. First person choose hat 3, Second person choose hat 4 and last one hat 5.
Example 2:
Input: hats = [[3,5,1],[3,5]] Output: 4 Explanation: There are 4 ways to choose hats: (3,5), (5,3), (1,3) and (1,5)
Example 3:
Input: hats = [[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]] Output: 24 Explanation: Each person can choose hats labeled from 1 to 4. Number of Permutations of (1,2,3,4) = 24.
Constraints:
n == hats.length1 <= n <= 101 <= hats[i].length <= 401 <= hats[i][j] <= 40hats[i] contains a list of unique integers.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The core idea is to try every single possible combination of hats assigned to people. We'll go through each person one by one, assigning them a hat from their list of possible hats. We'll check if the current assignment is valid, and if so, move on to the next person.
Here's how the algorithm would work step-by-step:
def number_ways(hats_for_person):
number_of_people = len(hats_for_person)
assigned_hats = [0] * number_of_people
valid_combinations_count = 0
def solve(person_index):
nonlocal valid_combinations_count
# Base case: All people have been assigned hats
if person_index == number_of_people:
valid_combinations_count += 1
return
# Iterate through each hat that the current person likes
for hat in hats_for_person[person_index]:
# Check if the hat is already being worn by someone else
is_hat_available = True
for previous_person in range(person_index):
if assigned_hats[previous_person] == hat:
is_hat_available = False
break
# If the hat is available, assign it to the current person
if is_hat_available:
assigned_hats[person_index] = hat
# Recursively try to assign hats to the next person
solve(person_index + 1)
# Backtrack: Unassign the hat to explore other possibilities.
assigned_hats[person_index] = 0
solve(0)
return valid_combinations_count
This problem asks us to count the number of ways people can wear hats, given each person has a list of hats they can wear. The trick is to use a counting method based on who is wearing a hat, instead of which hat is being worn, to drastically reduce the workload.
Here's how the algorithm would work step-by-step:
def number_of_ways_to_wear_hats(hats_for_person):
number_of_people = len(hats_for_person)
MOD = 10**9 + 7
hat_to_people = [[] for _ in range(41)]
for person_index, hats in enumerate(hats_for_person):
for hat in hats:
hat_to_people[hat].append(person_index)
# dp[mask] is the number of ways to assign hats to the people represented by the mask.
dp = [0] * (1 << number_of_people)
dp[0] = 1
for hat_index in range(1, 41):
next_dp = dp[:]
# Iterate through all possible assignment states.
for mask in range(1 << number_of_people):
# Consider each person who can wear the current hat.
for person_index in hat_to_people[hat_index]:
# If the person is not wearing a hat yet.
if not (mask & (1 << person_index)):
# Calculate the new mask.
new_mask = mask | (1 << person_index)
# Update the dp table.
next_dp[new_mask] = (next_dp[new_mask] + dp[mask]) % MOD
# Update dp for the next iteration.
dp = next_dp
# The result is the number of ways to assign hats to all people.
return dp[(1 << number_of_people) - 1]
| Case | How to Handle |
|---|---|
| Empty hats array | Return 0 because there are no possible arrangements if no one has any hats. |
| One person, with one or more hats | Return 1 because there's only one way for that person to wear a hat. |
| Many people, but some people have no hats | Return 0 because those people cannot wear a hat, making a valid arrangement impossible. |
| All people like the same set of hats | The number of ways to assign hats might be fewer than the number of permutations because the set is fixed. |
| Number of people is greater than the number of distinct hats | Return 0 because it's impossible to assign different hats to each person. |
| Hat list for each person has duplicate hat numbers | The duplicates will be treated as one unique hat for that person, so no special handling is needed. |
| The recursive calls create a large call stack that can cause stack overflow | Use dynamic programming (memoization) to store computed results to avoid recalculating and reduce recursion depth. |
| Integer overflow when calculating the number of ways | Take the modulo (10^9 + 7) after each multiplication or addition to prevent integer overflow. |