Stable Internships
Stable Internships
Category
Famous Algorithms
Difficulty
Medium
Problem Statement
Given a set of interns and a set of teams, where each intern has a ranked preference list of teams and each team has a ranked preference list of interns, find a stable matching between interns and teams. A matching is stable if there is no unmatched intern-team pair who would both prefer each other over their current match. This is the classic Stable Matching (Gale-Shapley) problem.
Intuition
The Gale-Shapley algorithm works by having one side (say, interns) "propose" to their most preferred unmatched team. Teams tentatively accept the best proposal they have received so far, rejecting the rest. Rejected interns then propose to their next choice. This process continues until everyone is matched.
The algorithm guarantees a stable matching: no intern-team pair exists where both would prefer each other over their assigned partner. The proposing side gets the best stable matching for them, while the receiving side gets their worst stable matching.
Approach
- Initialize all interns and teams as unmatched.
- While there exists an unmatched intern:
a. The intern proposes to the highest-ranked team they have not yet proposed to.
b. If the team is unmatched, they tentatively accept.
c. If the team is already matched but prefers this intern over their current match:
- The team switches to the new intern.
- The previously matched intern becomes unmatched. d. Otherwise, the team rejects the proposal.
- Return the final matching.
Pseudocode
function stableInternships(interns, teams):
n = number of interns (and teams)
// interns[i] = ranked list of team preferences for intern i
// teams[j] = ranked list of intern preferences for team j
// Precompute team preference rankings for O(1) comparison
teamRanks = 2D array
for j from 0 to n - 1:
for rank from 0 to n - 1:
teamRanks[j][teams[j][rank]] = rank
nextProposal = array of n zeros // next team index to propose to for each intern
teamMatch = array of n, all -1 // current intern matched to each team
freeInterns = stack of all intern indices
while freeInterns is not empty:
intern = freeInterns.pop()
team = interns[intern][nextProposal[intern]]
nextProposal[intern] += 1
if teamMatch[team] == -1:
teamMatch[team] = intern
else:
currentIntern = teamMatch[team]
if teamRanks[team][intern] < teamRanks[team][currentIntern]:
teamMatch[team] = intern
freeInterns.push(currentIntern)
else:
freeInterns.push(intern)
// Build result
result = []
for team from 0 to n - 1:
result.append([teamMatch[team], team])
return result
Time & Space Complexity
- Time: O(n^2), where n is the number of interns/teams. In the worst case, each intern proposes to every team. Precomputing rankings takes O(n^2).
- Space: O(n^2) for the preference lists and ranking matrices.
Key Insights
- The Gale-Shapley algorithm always terminates and produces a stable matching. This is proven by the fact that each intern proposes to each team at most once, giving at most n^2 proposals.
- The matching is "proposer-optimal": interns get the best partner they could in any stable matching.
- The matching is "receiver-pessimal": teams get the worst partner they could in any stable matching.
- Precomputing team ranking lookups (converting preference lists to rank maps) enables O(1) comparison during proposals.
- The algorithm is deterministic: the same input always produces the same output.
Edge Cases
- n = 1: trivial matching of the single intern to the single team.
- All interns have the same preference order: the algorithm still terminates and produces a stable matching.
- All teams have the same preference order: the top-preferred intern gets their first choice, etc.
- A scenario where no stable matching exists: this cannot happen; the Gale-Shapley algorithm always finds one.