Facebook Pixel

544. Output Contest Matches πŸ”’

MediumRecursionStringSimulation
Leetcode Link

Problem Description

This problem simulates the NBA playoff pairing system where teams are matched based on their rankings to create interesting matchups.

You are given n teams labeled from 1 to n, where team 1 is the strongest and team n is the weakest. The goal is to return a string representing the complete tournament bracket following a specific pairing strategy.

The pairing strategy works as follows:

  • In each round, pair the strongest remaining team with the weakest remaining team
  • The second strongest pairs with the second weakest, and so on
  • This continues until all teams are paired

The output format uses:

  • Parentheses () to show team pairings
  • Commas , to separate teams within a pairing

For example, with n = 4:

  • Round 1: Team 1 plays Team 4, Team 2 plays Team 3
  • This gives us (1,4) and (2,3)
  • Round 2: The winner bracket shows ((1,4),(2,3))

The algorithm simulates this tournament structure by:

  1. Starting with an array containing string representations of team numbers ["1", "2", "3", ..., "n"]
  2. In each round, pairing element at position i with element at position n-i-1
  3. Storing the paired result "(s[i],s[n-i-1])" back at position i
  4. Halving the active portion of the array and repeating until only one element remains
  5. The final element represents the complete tournament bracket

The bit shift operations (>> and >>=) are used to efficiently divide by 2, where n >> 1 means n / 2 and n >>= 1 means n = n / 2.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that this is essentially building a tournament bracket from the bottom up. When we pair strongest with weakest (1 with n, 2 with n-1, etc.), we're creating the first round of matches. The winners of these matches then need to be paired in the same way for the next round.

Think about what happens after the first round of pairing. We have n/2 match results. Now we need to pair these results again - the first result (which contains team 1) should pair with the last result (which contains team n), and so on. This pattern continues recursively.

The brilliant observation is that we can reuse the same array to store intermediate results. After creating the first round pairings, we only need the first n/2 positions of our array. We can overwrite these positions with the new pairings, treating each previous pairing as a single unit.

For example, with n = 8:

  • Start: ["1", "2", "3", "4", "5", "6", "7", "8"]
  • After round 1: ["(1,8)", "(2,7)", "(3,6)", "(4,5)", ...] (only first 4 positions matter now)
  • After round 2: ["((1,8),(4,5))", "((2,7),(3,6))", ...] (only first 2 positions matter)
  • After round 3: ["(((1,8),(4,5)),((2,7),(3,6)))", ...] (only first position matters)

Each round, we're essentially:

  1. Taking pairs from opposite ends of the active portion of the array
  2. Combining them with parentheses and comma
  3. Storing the result in the first half
  4. Reducing our working size by half

This naturally builds up the nested structure of parentheses that represents the complete tournament bracket, with the final result being a single string showing all matchups from every round.

Learn more about Recursion patterns.

Solution Approach

The solution uses a simulation approach with an array to build the tournament bracket string iteratively.

Data Structure:

  • We use an array s to store team representations, initially containing strings "1" through "n"

Algorithm Steps:

  1. Initialize the array: Create array s with n elements where s[i] = str(i + 1). This gives us ["1", "2", "3", ..., "n"].

  2. Iterative pairing process: We use a while loop that continues as long as n > 1:

    a. Pair teams in current round: For each position i from 0 to n/2 - 1:

    • Pair element at index i with element at index n - i - 1
    • Format as "(s[i],s[n-i-1])" and store back in s[i]
    • This ensures strongest pairs with weakest in each sub-bracket

    b. Reduce problem size: After pairing, divide n by 2 using bit shift (n >>= 1)

    • This reflects that we now have half as many "teams" (or match results) to process
  3. Return final result: When n becomes 1, s[0] contains the complete bracket structure

Example walkthrough with n = 4:

  • Initial: s = ["1", "2", "3", "4"], n = 4
  • Round 1 (i = 0): s[0] = "(1,4)", (i = 1): s[1] = "(2,3)"
  • After Round 1: s = ["(1,4)", "(2,3)", "3", "4"], n = 2
  • Round 2 (i = 0): s[0] = "((1,4),(2,3))"
  • After Round 2: s = ["((1,4),(2,3))", "(2,3)", "3", "4"], n = 1
  • Return s[0] = "((1,4),(2,3))"

Key Patterns:

  • The bit shift operation n >> 1 efficiently computes n / 2
  • The index calculation n - i - 1 always gives the corresponding element from the opposite end
  • We only modify and care about the first n/2 elements after each round, allowing in-place construction

The time complexity is O(n) as we process each team once per round, and there are log n rounds. The space complexity is O(n) for storing the array of strings.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with n = 8 teams to see how the tournament bracket is built step by step.

Initial Setup:

  • Array s = ["1", "2", "3", "4", "5", "6", "7", "8"]
  • n = 8 (we have 8 active elements to process)

Round 1: Creating First Round Matchups

  • We pair teams from opposite ends: strongest with weakest
  • i = 0: Pair s[0] with s[7] β†’ s[0] = "(1,8)"
  • i = 1: Pair s[1] with s[6] β†’ s[1] = "(2,7)"
  • i = 2: Pair s[2] with s[5] β†’ s[2] = "(3,6)"
  • i = 3: Pair s[3] with s[4] β†’ s[3] = "(4,5)"
  • Array becomes: ["(1,8)", "(2,7)", "(3,6)", "(4,5)", "5", "6", "7", "8"]
  • Update n = 4 (we now have 4 match results to process)

Round 2: Pairing Winners of Round 1

  • Now we pair the match results from opposite ends
  • i = 0: Pair s[0] with s[3] β†’ s[0] = "((1,8),(4,5))"
  • i = 1: Pair s[1] with s[2] β†’ s[1] = "((2,7),(3,6))"
  • Array becomes: ["((1,8),(4,5))", "((2,7),(3,6))", "(3,6)", "(4,5)", ...]
  • Update n = 2 (we now have 2 semi-final results)

Round 3: Final Championship Pairing

  • i = 0: Pair s[0] with s[1] β†’ s[0] = "(((1,8),(4,5)),((2,7),(3,6)))"
  • Array becomes: ["(((1,8),(4,5)),((2,7),(3,6)))", "((2,7),(3,6))", ...]
  • Update n = 1 (tournament complete)

Final Result: Return s[0] = "(((1,8),(4,5)),((2,7),(3,6)))"

This string represents the complete tournament bracket where:

  • Teams 1 and 8 play each other, as do 4 and 5 (forming one side of the bracket)
  • Teams 2 and 7 play each other, as do 3 and 6 (forming the other side)
  • Winners advance through the bracket structure shown by the nested parentheses

Solution Implementation

1class Solution:
2    def findContestMatch(self, n: int) -> str:
3        # Initialize array with string representations of teams 1 to n
4        teams = [str(i + 1) for i in range(n)]
5      
6        # Continue pairing until only one match remains
7        while n > 1:
8            # Pair teams from both ends: strongest with weakest
9            for i in range(n // 2):
10                # Create match pairing: team at index i with team at index (n-i-1)
11                teams[i] = f"({teams[i]},{teams[n - i - 1]})"
12          
13            # Halve the number of teams for next round
14            n //= 2
15      
16        # Return the final tournament bracket structure
17        return teams[0]
18
1class Solution {
2    /**
3     * Generates the contest match pairing string for n teams.
4     * Teams are paired such that strongest plays weakest in each round.
5     * 
6     * @param n The number of teams (guaranteed to be a power of 2)
7     * @return The final pairing string showing the tournament bracket
8     */
9    public String findContestMatch(int n) {
10        // Initialize array to store team representations
11        // Initially, each element contains just the team number
12        String[] teams = new String[n];
13        for (int i = 0; i < n; i++) {
14            teams[i] = String.valueOf(i + 1);
15        }
16      
17        // Simulate tournament rounds by pairing teams
18        // In each round, the number of active teams is halved
19        while (n > 1) {
20            // Pair teams: first with last, second with second-last, etc.
21            for (int i = 0; i < n / 2; i++) {
22                // Create a match between team at index i and its complement
23                // The complement is at position (n - i - 1)
24                teams[i] = String.format("(%s,%s)", teams[i], teams[n - i - 1]);
25            }
26          
27            // Halve the number of teams for the next round
28            n = n >> 1;  // Equivalent to n = n / 2
29        }
30      
31        // Return the final bracket structure stored at index 0
32        return teams[0];
33    }
34}
35
1class Solution {
2public:
3    string findContestMatch(int n) {
4        // Initialize a vector to store string representations of team numbers
5        vector<string> teams(n);
6      
7        // Fill the vector with team numbers from 1 to n
8        for (int i = 0; i < n; ++i) {
9            teams[i] = to_string(i + 1);
10        }
11      
12        // Continue pairing teams until only one remains
13        // Each iteration reduces the number of teams by half
14        while (n > 1) {
15            // Pair the strongest with the weakest team
16            // i-th team pairs with (n-i-1)-th team
17            for (int i = 0; i < n / 2; ++i) {
18                teams[i] = "(" + teams[i] + "," + teams[n - i - 1] + ")";
19            }
20          
21            // Halve the number of teams for the next round
22            n >>= 1;
23        }
24      
25        // Return the final bracket structure
26        return teams[0];
27    }
28};
29
1/**
2 * Generates a contest match pairing string for n teams using tournament bracket format.
3 * Teams are paired such that strongest team (1) plays weakest team (n), 
4 * second strongest (2) plays second weakest (n-1), and so on.
5 * 
6 * @param n - The number of teams (must be a power of 2)
7 * @returns A string representing the final bracket structure
8 */
9function findContestMatch(n: number): string {
10    // Initialize array with team numbers as strings (1 to n)
11    const teamBrackets: string[] = Array.from(
12        { length: n }, 
13        (_, index) => (index + 1).toString()
14    );
15  
16    // Keep pairing teams until only one bracket remains
17    // Each iteration reduces the number of active brackets by half
18    for (let remainingTeams = n; remainingTeams > 1; remainingTeams >>= 1) {
19        // Pair up teams: first with last, second with second-last, etc.
20        for (let i = 0; i < remainingTeams >> 1; ++i) {
21            // Create a match pairing between team at index i and its mirror opponent
22            teamBrackets[i] = `(${teamBrackets[i]},${teamBrackets[remainingTeams - i - 1]})`;
23        }
24    }
25  
26    // Return the final bracket structure stored at index 0
27    return teamBrackets[0];
28}
29

Time and Space Complexity

Time Complexity: O(n log n)

The algorithm uses a while loop that continues while n > 1. In each iteration:

  • The inner for loop runs n/2 times (since i ranges from 0 to n >> 1)
  • Each iteration performs a constant-time string concatenation operation
  • After each outer loop iteration, n is halved (n >>= 1)

The total work done is:

  • First iteration: n/2 operations
  • Second iteration: n/4 operations
  • Third iteration: n/8 operations
  • ...
  • Last iteration: 1 operation

This forms a geometric series: n/2 + n/4 + n/8 + ... + 1 = n - 1 β‰ˆ O(n)

However, we must also consider the string concatenation cost. As the tournament progresses, the strings grow longer. The maximum string length grows proportionally to O(n) in the worst case (containing all team numbers and brackets). String concatenation has a cost proportional to the length of the resulting string.

Considering both factors:

  • Number of iterations: log n (since we halve n each time)
  • Work per iteration: O(n) (combining the number of operations and string manipulation costs)
  • Total: O(n log n)

Space Complexity: O(n)

The space is used for:

  • The list s which initially stores n strings: O(n)
  • As the algorithm progresses, we modify the list in-place, reusing the first half of the array
  • The strings themselves grow in total size but the combined length of all team representations and brackets is bounded by O(n)
  • Therefore, the total space complexity is O(n)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Array Indexing After Halving n

A common mistake is accessing invalid array indices after reducing n. While we update n //= 2 after each round, the actual array size remains unchanged. This can lead to confusion about which elements are "active" in subsequent iterations.

Problematic approach:

# Wrong: Trying to create a new smaller array each round
while n > 1:
    new_teams = []
    for i in range(n // 2):
        new_teams.append(f"({teams[i]},{teams[n - i - 1]})")
    teams = new_teams  # Unnecessary array recreation
    n //= 2

Solution: Use the same array throughout and only process the first n elements, treating n as the logical size rather than physical size.

2. Off-by-One Error in Pairing Logic

The pairing formula n - i - 1 is crucial. A common mistake is using n - i instead, which would incorrectly pair teams and potentially cause index out of bounds errors.

Wrong pairing:

teams[i] = f"({teams[i]},{teams[n - i]})"  # Error when i = 0, accesses teams[n]

Correct pairing:

teams[i] = f"({teams[i]},{teams[n - i - 1]})"  # Correctly pairs opposite ends

3. Not Handling Powers of 2 Constraint

The problem assumes n is a power of 2 (2, 4, 8, 16, etc.). If you try to generalize for non-power-of-2 inputs without proper handling, the algorithm breaks.

Issue example with n = 6:

  • First round: pairs (1,6), (2,5), (3,4)
  • After n //= 2, we have n = 3
  • Next round tries to pair 3 teams, but 3 is odd!

Solution: Either validate input or add special handling for non-power-of-2 cases:

# Validate input
if n & (n - 1) != 0:  # Check if n is not a power of 2
    raise ValueError("n must be a power of 2")

4. String Concatenation Performance

While not incorrect, using string concatenation in a loop can be inefficient for large inputs.

Less efficient:

teams[i] = "(" + teams[i] + "," + teams[n - i - 1] + ")"

More efficient:

teams[i] = f"({teams[i]},{teams[n - i - 1]})"  # f-string is cleaner and faster

5. Modifying Loop Variable Inside Loop

Some might try to modify n inside the pairing loop, which would cause incorrect behavior:

Wrong:

while n > 1:
    for i in range(n // 2):
        teams[i] = f"({teams[i]},{teams[n - i - 1]})"
        n -= 1  # Don't modify n here!

Correct: Only modify n after completing all pairings for the current round.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement recursion?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More