Construct the Lexicographically Largest Valid Sequence

Updated on 09 May, 2025
Construct the Lexicographically Largest Valid Sequence header image

Problem Statement

Given an integer n, your task is to construct a sequence using numbers from 1 to n adhering to specific conditions. In the sequence:

  • The number 1 should appear only once.
  • Every number from 2 to n must appear exactly twice.
  • For each number i ranging from 2 to n, the distance between its two occurrences should be precisely i. The distance is defined by the absolute difference between their positions in the sequence.

Your goal is to ensure that this constructed sequence is the lexicographically largest possible sequence meeting the above criteria. A sequence is considered lexicographically larger if at the first differing position, it possesses a larger number compared to another sequence of the same length. This task is guaranteed to have a solution within the given constraints.

Examples

Example 1

Input:

n = 3

Output:

[3,1,2,3,2]

Explanation:

[2,3,2,1,3] is also a valid sequence, but [3,1,2,3,2] is the lexicographically largest valid sequence.

Example 2

Input:

n = 5

Output:

[5,3,1,4,3,5,2,4,2]

Constraints

  • 1 <= n <= 20

Approach and Intuition

To generate the lexicographically largest sequence satisfying the given conditions, it's beneficial to strategically place the numbers starting from the largest. Here's a step-by-step breakdown of the approach:

  1. Start by initializing an empty sequence and a placement tracker to ensure that each condition about the distances is met effectively.
  2. Iterate from the largest number (n) down to 2:
    • Attempt to place each occurrence of the number i such that the first occurrence is as far to the right as possible but leaves enough space for the second occurrence to satisfy the distance condition i.
    • If placing i at certain positions results in a conflict (i.e., an overlap with another number or not enough space), adjust its position.
  3. Once all numbers from 2 to n are placed according to their required distances, place the number 1 in the remaining slot, which should be the first unfilled position from the left due to its single occurrence requirement.
  4. You may utilize a backtracking method combined with these strategic placements:
    • Start filling the sequence from the first position possible for the largest number and backtrack whenever a placement violates the sequence conditions or leads to a dead end.
    • The search will continue until a valid sequence is constructed that adheres to all the conditions.

This thoughtful approach, beginning from the largest and placing strategically backward, should yield the lexicographically largest sequence as it prioritizes larger numbers to fill earlier and more significant positions in the sequence, wherever applicable.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    vector<int> createSequence(int n) {
        vector<int> sequence(2 * n - 1, 0);
        vector<bool> used(n + 1, false);
        backtrack(0, sequence, used, n);
        return sequence;
    }

private:
    bool backtrack(int idx, vector<int>& sequence, vector<bool>& used, int n) {
        if (idx == sequence.size()) {
            return true;
        }
        if (sequence[idx] != 0) {
            return backtrack(idx + 1, sequence, used, n);
        }
        for (int num = n; num > 0; --num) {
            if (used[num]) continue;

            used[num] = true;
            sequence[idx] = num;

            if (num == 1) {
                if (backtrack(idx + 1, sequence, used, n)) {
                    return true;
                }
            } else if (idx + num < sequence.size() && sequence[idx + num] == 0) {
                sequence[idx + num] = num;
                if (backtrack(idx + 1, sequence, used, n)) {
                    return true;
                }
                sequence[idx + num] = 0;
            }

            sequence[idx] = 0;
            used[num] = false;
        }
        return false;
    }
};

Create a lexicographically largest valid sequence for a given integer n using C++. Implement the sequence in such a way that each integer from 1 to n appears exactly once, and for each integer greater than 1, there is exactly one gap of its own value between its two instances.

  • Use the C++ standard vector to manage the sequence and track used numbers.
  • Start by initializing your sequence vector with 2 * n - 1 zeros to accommodate the largest possible sequence given the constraints.
  • Keep track of numbers that have already been used by employing a boolean vector, initialized to false.
  • Utilize backtracking to explore all possible placements of numbers:
    1. Exit the recursion successfully if all positions in the sequence are filled appropriately.
    2. If the current sequence position already has a number, move to the next position.
    3. Iterate over the numbers from n to 1, choosing a number if it has not been used yet:
      • Mark the number as used and assign it to the current position in the sequence.
      • For numbers equal to 1, simply move to the next position.
      • For numbers greater than 1, place another instance of the number at an index equal to the current plus the number itself, ensuring you do not exceed the sequence bounds or overwrite another number.
      • If successful, continue; otherwise, revert the changes and try the next possibility.
    4. Restore the original state if no valid sequence configuration is found for the current setup.
  • Returns the generated sequence once all positions are filled correctly.

This approach efficiently constructs the largest sequence by prioritizing the filling of high numbers first and backtracking in case of failed placements.

java
public class Solution {

    public int[] generateSequence(int maxNumber) {
        int[] sequence = new int[maxNumber * 2 - 1];
        boolean[] usedNumbers = new boolean[maxNumber + 1];

        backtrack(
            0,
            sequence,
            usedNumbers,
            maxNumber
        );

        return sequence;
    }

    private boolean backtrack(
        int position,
        int[] sequence,
        boolean[] usedNumbers,
        int maxNumber
    ) {
        if (position == sequence.length) {
            return true;
        }

        if (sequence[position] != 0) {
            return backtrack(
                position + 1,
                sequence,
                usedNumbers,
                maxNumber
            );
        }

        for (int number = maxNumber; number >= 1; number--) {
            if (usedNumbers[number]) continue;

            usedNumbers[number] = true;
            sequence[position] = number;

            if (number == 1) {
                if (backtrack(
                    position + 1,
                    sequence,
                    usedNumbers,
                    maxNumber
                )) {
                    return true;
                }
            } else if (
                position + number < sequence.length &&
                sequence[position + number] == 0
            ) {
                sequence[position + number] = number;

                if (backtrack(
                    position + 1,
                    sequence,
                    usedNumbers,
                    maxNumber
                )) {
                    return true;
                }

                sequence[position + number] = 0;
            }

            sequence[position] = 0;
            usedNumbers[number] = false;
        }

        return false;
    }
}

This Java solution aims to construct the lexicographically largest valid sequence given a maximum number maxNumber. The sequence created has a length of maxNumber * 2 - 1, ensuring that it uses the maximum possible space while adhering to sequence validity. Here's a functional breakdown of the solution:

  • Data Structures

    • sequence[] holds the final sequence results.
    • usedNumbers[] tracks the numbers that are already used in the sequence.
  • Methodology

    • The generateSequence(int maxNumber) method initializes structures and starts the recursive backtracking with backtrack().
  • Backtracking Approach

    • The backtrack() method processes each position of the sequence array:
      • If the current sequence position is already filled, the function recurses to the next position.
      • It tries to place each number (starting from maxNumber down to 1) in the sequence:
        • Marks the number as used.
        • Sets the current position in the sequence array to this number.
        • For numbers greater than 1, it also tries to fill the corresponding mirrored position in the sequence with the same number to maintain validity.
        • Recurses to fill the next position.
        • If stuck (no valid sequence from current configuration), it backtracks by resetting changes and continues with the next potential number.
  • Completion Check

    • If it reaches the end of the sequence array successfully, it indicates that a valid sequence has been formed.
  • Recursive Base Case

    • When position == sequence.length, it indicates all positions are correctly filled, and it returns true up the recursion stack.

Execute the generateSequence() method with the desired maxNumber to get the lexicographically largest valid sequence for that number range. This setup meticulously ensures that all configurations are considered and the highest possible number placement is prioritized, leveraging recursion and backtracking efficiently.

python
class Solution:
    def generate_sequence(self, n: int) -> List[int]:
        # Create a list with space for 2*n - 1 elements initialized to zero
        sequence = [0] * (2 * n - 1)
        used = [False] * (n + 1)

        # Begin depth-first search to fill the sequence
        self.populating_sequence(
            0, sequence, used, n
        )

        return sequence
    
    def populating_sequence(
        self, idx, sequence, used, n
    ):
        # Success upon filling up the sequence
        if idx == len(sequence):
            return True
        
        # Skip already filled indices
        if sequence[idx] != 0:
            return self.populating_sequence(
                idx + 1,
                sequence,
                used,
                n,
            )
        
        # Place integers in descending order for maximal results
        for num in range(n, 0, -1):
            if used[num]:
                continue

            used[num] = True
            sequence[idx] = num

            # Quick placement for 1 since it needs no special index spacing
            if num == 1:
                if self.populating_sequence(
                    idx + 1,
                    sequence,
                    used,
                    n,
                ):
                    return True
            else:
                # Ensure space for indexing based on number placement
                if idx + num < len(sequence) and sequence[idx + num] == 0:
                    sequence[idx + num] = num

                    if self.populating_sequence(
                        idx + 1,
                        sequence,
                        used,
                        n,
                    ):
                        return True

                    # Revert move if not successful
                    sequence[idx + num] = 0

            # Reset for backtracking
            sequence[idx] = 0
            used[num] = False

        return False

The given Python solution involves constructing the lexicographically largest valid sequence given an integer n. The sequence's length is predetermined as 2*n - 1. This solution employs a depth-first search strategy, which recursively attempts to populate the sequence with the largest values from n to 1 while ensuring the positional constraints are met.

  • Start by initializing:

    • sequence, a list of length 2*n - 1 filled with zeros, to store the sequence.
    • used, a Boolean list of length n + 1 to track which numbers have been placed in the sequence.
  • Implement populating_sequence method to populate the sequence:

    • If reaching an index that’s already filled, attempt to populate the next index.
    • Work in descending order from n to ensure the sequence is lexicographically largest.
    • For each number, check if it has not been used:
      • Place the number at the current index.
      • If the number is not 1, check if it can legally be placed again at index + number. If so, proceed to fill the next index recursively.
      • If sequence cannot be populated successfully after placing a number, backtrack by resetting the number's positions.
  • A crucial part of the algorithm is the use of backtracking to revert choices when a complete and valid sequence cannot be constructed from the current state of sequence and used.

This approach ensures that the sequence generated is the largest possible lexicographically under the given rules, through strategic placement and systematic backtracking.

Comments

No comments yet.