Facebook Pixel

899. Orderly Queue

Problem Description

You have a string s and an integer k. You can perform the following operation any number of times:

  • Choose one of the first k letters in the string
  • Remove it from its current position
  • Append it to the end of the string

Your goal is to find the lexicographically smallest string possible after performing any number of these operations.

For example, if s = "cba" and k = 1, you can only move the first character each time:

  • Starting with "cba"
  • Move 'c' to end: "bac"
  • Move 'b' to end: "acb"
  • Move 'a' to end: "cba" (back to original)

The smallest string achievable would be "acb".

If k ≥ 2, you have more flexibility. You can choose from the first 2 (or more) characters to move, which gives you the ability to rearrange the string more freely.

The key insight is:

  • When k = 1, you can only rotate the string, so you need to check all possible rotations and return the smallest one
  • When k > 1, you can actually achieve any permutation of the string through a series of moves, so the answer is simply the sorted string
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what operations we can actually perform with different values of k.

When k = 1, we're severely limited - we can only take the first character and move it to the end. This is essentially a rotation operation. If we keep doing this, we'll cycle through all possible rotations of the string. For a string of length n, after n operations we're back where we started. So with k = 1, we can only achieve n different strings (all rotations), and we need to find the lexicographically smallest one.

When k = 2, something interesting happens. We can now choose between the first two characters to move. This gives us much more power. Consider what happens when we want to swap two adjacent characters in the middle of the string, say we want to swap x and y in abc[xy]def:

  1. Move a, b, c to the end one by one: [xy]defabc
  2. Now x and y are at the front. Move y to the end: [x]defabcy
  3. Move x to the end: defabcy[x]
  4. Move d, e, f to the end: abcy[x]def
  5. Continue rotating until we get: abc[yx]def

We've successfully swapped x and y! This means with k ≥ 2, we can swap any two adjacent characters.

And if we can swap adjacent characters, we can perform bubble sort! We can keep swapping adjacent characters until the entire string is sorted. This is why when k > 1, the answer is simply the sorted version of the string - it's the lexicographically smallest possible arrangement, and we can achieve it through a series of moves.

The solution elegantly handles both cases: rotate through all possibilities when k = 1, or return the sorted string when k > 1.

Learn more about Math and Sorting patterns.

Solution Approach

The solution implements a case-by-case approach based on the value of k:

Case 1: When k = 1

Since we can only move the first character to the end, we need to check all possible rotations of the string. The implementation:

  1. Initialize ans with the original string s
  2. Perform len(s) - 1 rotations (we don't need to do all n rotations since the last one would bring us back to the original)
  3. In each iteration:
    • Move the first character to the end: s = s[1:] + s[0]
    • Compare with the current answer and keep the smaller one: ans = min(ans, s)
  4. Return the lexicographically smallest string found
if k == 1:
    ans = s
    for _ in range(len(s) - 1):
        s = s[1:] + s[0]
        ans = min(ans, s)
    return ans

Case 2: When k > 1

As established in the intuition, when k > 1, we can achieve any permutation of the string. The lexicographically smallest permutation is simply the sorted string.

return "".join(sorted(s))

The sorted(s) function converts the string into a list of characters, sorts them in ascending order, and "".join() concatenates them back into a string.

Time Complexity:

  • For k = 1: O(n²) where n is the length of the string (we perform n rotations, each taking O(n) time for string operations and comparison)
  • For k > 1: O(n log n) due to sorting

Space Complexity:

  • For k = 1: O(n) for storing the answer string
  • For k > 1: O(n) for the sorted character list

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through two examples to illustrate how the solution works for different values of k.

Example 1: s = "dcba", k = 1

Since k = 1, we can only move the first character to the end. Let's trace through all rotations:

  1. Start with "dcba" (initialize ans = "dcba")
  2. First rotation: Move 'd' to end → "cbad"
    • Compare: min("dcba", "cbad") = "cbad" (update ans = "cbad")
  3. Second rotation: Move 'c' to end → "badc"
    • Compare: min("cbad", "badc") = "badc" (update ans = "badc")
  4. Third rotation: Move 'b' to end → "adcb"
    • Compare: min("badc", "adcb") = "adcb" (update ans = "adcb")

We've checked all meaningful rotations. The answer is "adcb".

Example 2: s = "dcba", k = 2

Since k > 1, we can achieve any permutation of the string through a series of moves. The lexicographically smallest permutation is the sorted string.

  1. Sort the characters: ['d', 'c', 'b', 'a'] → ['a', 'b', 'c', 'd']
  2. Join them: "abcd"

The answer is "abcd".

Notice how with k = 2, we get a better result ("abcd") than with k = 1 ("adcb"). This demonstrates the increased flexibility when we can choose from multiple characters at the front of the string.

Solution Implementation

1class Solution:
2    def orderlyQueue(self, s: str, k: int) -> str:
3        """
4        Find the lexicographically smallest string that can be obtained by repeatedly:
5        - Choose one of the first k letters and move it to the end of the string
6      
7        Args:
8            s: Input string to be rearranged
9            k: Number of first letters we can choose from to move to the end
10          
11        Returns:
12            Lexicographically smallest string possible
13        """
14      
15        if k == 1:
16            # When k=1, we can only rotate the string
17            # Try all possible rotations and find the minimum
18            min_string = s
19          
20            # Generate all rotations by moving first character to end
21            for _ in range(len(s) - 1):
22                s = s[1:] + s[0]  # Move first character to end
23                min_string = min(min_string, s)  # Keep track of minimum
24              
25            return min_string
26      
27        else:
28            # When k >= 2, we can achieve any permutation
29            # The smallest permutation is the sorted string
30            return "".join(sorted(s))
31
1class Solution {
2    public String orderlyQueue(String s, int k) {
3        // When k = 1, we can only rotate the string
4        // We need to find the lexicographically smallest rotation
5        if (k == 1) {
6            String minString = s;
7            StringBuilder rotatedString = new StringBuilder(s);
8          
9            // Try all possible rotations by moving first character to the end
10            for (int i = 0; i < s.length() - 1; i++) {
11                // Move first character to the end
12                rotatedString.append(rotatedString.charAt(0)).deleteCharAt(0);
13              
14                // Update minimum if current rotation is lexicographically smaller
15                if (rotatedString.toString().compareTo(minString) < 0) {
16                    minString = rotatedString.toString();
17                }
18            }
19            return minString;
20        }
21      
22        // When k >= 2, we can achieve any permutation of the string
23        // The lexicographically smallest permutation is the sorted string
24        char[] charArray = s.toCharArray();
25        Arrays.sort(charArray);
26        return String.valueOf(charArray);
27    }
28}
29
1class Solution {
2public:
3    string orderlyQueue(string s, int k) {
4        // When k = 1, we can only rotate the string
5        // We need to find the lexicographically smallest rotation
6        if (k == 1) {
7            string minString = s;
8          
9            // Try all possible rotations by moving first character to the end
10            for (int i = 0; i < s.size() - 1; ++i) {
11                // Rotate: move first character to the end
12                s = s.substr(1) + s[0];
13              
14                // Update minimum if current rotation is smaller
15                if (s < minString) {
16                    minString = s;
17                }
18            }
19          
20            return minString;
21        }
22      
23        // When k >= 2, we can achieve any permutation of the string
24        // The smallest permutation is the sorted string
25        sort(s.begin(), s.end());
26        return s;
27    }
28};
29
1/**
2 * Finds the lexicographically smallest string that can be obtained by performing operations on string s.
3 * Operation: Choose one of the first k letters and move it to the end of the string.
4 * 
5 * @param s - The input string to be rearranged
6 * @param k - The number of first letters that can be chosen for the operation
7 * @returns The lexicographically smallest string possible
8 */
9function orderlyQueue(s: string, k: number): string {
10    // When k > 1, we can achieve any permutation of the string
11    // Therefore, return the sorted string (lexicographically smallest)
12    if (k > 1) {
13        const charactersArray: string[] = [...s];
14        charactersArray.sort();
15        return charactersArray.join('');
16    }
17  
18    // When k = 1, we can only rotate the string
19    // Try all possible rotations and find the lexicographically smallest one
20    const stringLength: number = s.length;
21    let lexicographicallySmallest: string = s;
22  
23    // Generate all rotations by moving characters from front to back
24    for (let rotationIndex = 1; rotationIndex < stringLength; rotationIndex++) {
25        // Create rotation: take substring from index i to end + substring from start to index i
26        const currentRotation: string = s.slice(rotationIndex) + s.slice(0, rotationIndex);
27      
28        // Update minimum if current rotation is lexicographically smaller
29        if (currentRotation < lexicographicallySmallest) {
30            lexicographicallySmallest = currentRotation;
31        }
32    }
33  
34    return lexicographicallySmallest;
35}
36

Time and Space Complexity

The time complexity depends on the value of k:

  • When k = 1: The code performs n-1 rotations of the string, where each rotation involves string slicing and concatenation operations that take O(n) time. Additionally, each comparison with min() takes O(n) time. Therefore, the total time complexity is O(n^2).

  • When k > 1: The code simply sorts the string using sorted(), which has a time complexity of O(n log n).

Overall, the worst-case time complexity is O(n^2) when k = 1.

The space complexity is O(n) in both cases:

  • When k = 1: The variable ans stores a copy of the string, and string slicing operations create new strings, requiring O(n) space.
  • When k > 1: The sorted() function creates a new list of characters requiring O(n) space, and join() creates a new string of length n.

Therefore, the time complexity is O(n^2) and the space complexity is O(n), where n is the length of the string.

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

Common Pitfalls

Pitfall 1: Misunderstanding the k > 1 Case

The Mistake: Many people initially think that when k = 2, you can only perform limited rearrangements, similar to k = 1. They might try to implement complex logic to track which characters can be moved based on the current first k positions.

Why It Happens: The problem statement makes it seem like k = 2 is just slightly more flexible than k = 1, but the reality is that k ≥ 2 allows you to achieve ANY permutation of the string through a series of moves.

The Mathematical Insight: When k ≥ 2, you can effectively "bubble sort" the string:

  • You can always bring any character to the front by strategically moving other characters to the back
  • This is possible because with 2 positions to choose from, you can preserve one character while cycling others

Solution: Recognize that for k ≥ 2, the answer is always the sorted string:

if k >= 2:
    return "".join(sorted(s))

Pitfall 2: Inefficient Rotation Implementation for k = 1

The Mistake: Using string concatenation in a loop without considering the number of iterations needed:

# Inefficient: Checking all n rotations when n-1 is sufficient
ans = s
for i in range(len(s)):  # Should be range(len(s) - 1)
    s = s[1:] + s[0]
    ans = min(ans, s)

Why It Happens: After n rotations (where n = len(s)), the string returns to its original form. The last rotation is redundant.

Solution: Only perform n-1 rotations since the n-th rotation brings you back to the original string:

ans = s
for _ in range(len(s) - 1):  # Note: len(s) - 1, not len(s)
    s = s[1:] + s[0]
    ans = min(ans, s)

Pitfall 3: Not Considering the Original String for k = 1

The Mistake: Forgetting to include the original string in the comparison:

# Wrong: Starting with empty or first rotation
ans = ""  # or ans = s[1:] + s[0]
for _ in range(len(s)):
    s = s[1:] + s[0]
    if not ans or s < ans:
        ans = s

Why It Happens: The original string might already be the lexicographically smallest rotation, and skipping it would give an incorrect answer.

Solution: Always initialize the answer with the original string before starting rotations:

ans = s  # Start with original string
for _ in range(len(s) - 1):
    s = s[1:] + s[0]
    ans = min(ans, s)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

A heap is a ...?


Recommended Readings

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

Load More