Facebook Pixel

1859. Sorting the Sentence

Problem Description

You are given a sentence that has been shuffled in a special way. Originally, the sentence was a normal list of words separated by single spaces with no leading or trailing spaces. Each word contains only lowercase and uppercase English letters.

The shuffling process works like this: each word gets a number (1, 2, 3, etc.) appended to its end based on its position in the original sentence (1 for the first word, 2 for the second word, and so on). Then, all these modified words are rearranged randomly.

For example:

  • Original sentence: "This is a sentence"
  • After adding position numbers: "This1 is2 a3 sentence4"
  • After shuffling: "sentence4 a3 is2 This1" or "is2 sentence4 This1 a3" or any other arrangement

Your task is to take a shuffled sentence s (containing no more than 9 words) and reconstruct the original sentence by:

  1. Identifying the position number at the end of each word
  2. Placing each word back in its correct position (without the number)
  3. Returning the reconstructed sentence as a string

The solution approach splits the shuffled sentence into words, extracts the position number from the last character of each word, removes that number from the word, and places the word at its correct position in the result array. The position number at the end of each word tells us exactly where that word should go in the original sentence (remembering that positions are 1-indexed, so we need to subtract 1 for array indexing). Finally, all words are joined back together with spaces to form the original sentence.

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

Intuition

Since each word in the shuffled sentence has its original position number attached at the end, we have all the information needed to reconstruct the original sentence. The key insight is that the number at the end of each word acts like a label telling us exactly where that word belongs.

Think of it like having a jigsaw puzzle where each piece has a number written on it telling you its exact position. You don't need to figure out which piece goes where through trial and error - you just look at the number and place it accordingly.

The process becomes straightforward:

  1. We know each word ends with a digit (1-9) that represents its position
  2. We can extract this digit to know where to place the word
  3. We can remove this digit to get the original word back

For example, when we see "sentence4", we immediately know:

  • The word is "sentence" (everything except the last character)
  • It belongs at position 4 in the original sentence
  • Since arrays are 0-indexed, we place it at index 3 (4 - 1)

By creating an array with the same number of slots as we have words, we can directly place each word into its correct position as we process them. This eliminates the need for sorting - we're essentially doing a direct placement based on the embedded position information.

The beauty of this approach is its simplicity: we're not actually sorting anything despite the problem appearing to be about reordering. We're just reading the instructions that are already embedded in each word and following them directly.

Learn more about Sorting patterns.

Solution Approach

The implementation follows a direct placement strategy using an array to reconstruct the original sentence:

Step 1: Split the shuffled sentence

ws = s.split()

We split the input string s by spaces to get an array of shuffled words. For example, "sentence4 a3 is2 This1" becomes ["sentence4", "a3", "is2", "This1"].

Step 2: Initialize a result array

ans = [None] * len(ws)

We create an array ans with the same length as the number of words. This array will hold the words in their correct positions. Using None as placeholders ensures we have the right number of slots.

Step 3: Process each word and place it correctly

for w in ws:
    ans[int(w[-1]) - 1] = w[:-1]

For each word w in our shuffled words array:

  • w[-1] extracts the last character (the position digit)
  • int(w[-1]) converts this character to an integer
  • int(w[-1]) - 1 adjusts from 1-indexed to 0-indexed for array placement
  • w[:-1] extracts everything except the last character (the original word)
  • We place the original word at its correct position in the ans array

For example, processing "sentence4":

  • w[-1] gives us '4'
  • int('4') - 1 = 3 (the array index)
  • w[:-1] gives us "sentence"
  • We place "sentence" at ans[3]

Step 4: Join the words back together

return " ".join(ans)

Finally, we join all the words in the ans array with spaces to create the reconstructed original sentence.

The time complexity is O(n) where n is the length of the input string, as we process each word once. The space complexity is also O(n) for storing the split words and the result array.

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 a concrete example with the shuffled sentence: "is2 sentence4 This1 a3"

Initial State:

  • Input: "is2 sentence4 This1 a3"
  • We need to reconstruct: "This is a sentence"

Step 1: Split the sentence

ws = ["is2", "sentence4", "This1", "a3"]

Step 2: Create result array

ans = [None, None, None, None]  # 4 slots for 4 words

Step 3: Process each word

Processing "is2":

  • Last character: '2' β†’ position 2
  • Array index: 2 - 1 = 1
  • Word without number: "is"
  • Place at index 1: ans = [None, "is", None, None]

Processing "sentence4":

  • Last character: '4' β†’ position 4
  • Array index: 4 - 1 = 3
  • Word without number: "sentence"
  • Place at index 3: ans = [None, "is", None, "sentence"]

Processing "This1":

  • Last character: '1' β†’ position 1
  • Array index: 1 - 1 = 0
  • Word without number: "This"
  • Place at index 0: ans = ["This", "is", None, "sentence"]

Processing "a3":

  • Last character: '3' β†’ position 3
  • Array index: 3 - 1 = 2
  • Word without number: "a"
  • Place at index 2: ans = ["This", "is", "a", "sentence"]

Step 4: Join with spaces

Result: "This is a sentence"

Each word finds its home directly based on the number attached to it - no sorting needed, just direct placement into the correct position.

Solution Implementation

1class Solution:
2    def sortSentence(self, s: str) -> str:
3        """
4        Reconstructs a sentence by sorting words based on their trailing position numbers.
5      
6        Args:
7            s: A shuffled sentence where each word ends with its position number (1-indexed)
8      
9        Returns:
10            The original sentence with words in correct order and position numbers removed
11        """
12        # Split the sentence into individual words
13        words = s.split()
14      
15        # Initialize result array with size equal to number of words
16        result = [None] * len(words)
17      
18        # Process each word
19        for word in words:
20            # Extract position from last character (convert to 0-indexed)
21            position = int(word[-1]) - 1
22          
23            # Place word (without position number) at correct index
24            result[position] = word[:-1]
25      
26        # Join words back into a sentence
27        return " ".join(result)
28
1class Solution {
2    public String sortSentence(String s) {
3        // Split the sentence into individual words
4        String[] words = s.split(" ");
5        int wordCount = words.length;
6      
7        // Create an array to store words in their correct positions
8        String[] sortedWords = new String[wordCount];
9      
10        // Process each word to extract its position and content
11        for (int i = 0; i < wordCount; ++i) {
12            String currentWord = words[i];
13          
14            // Get the last character (position digit) and convert to index (0-based)
15            int position = currentWord.charAt(currentWord.length() - 1) - '1';
16          
17            // Extract the word without the position digit and place it at correct index
18            sortedWords[position] = currentWord.substring(0, currentWord.length() - 1);
19        }
20      
21        // Join the sorted words back into a sentence with spaces
22        return String.join(" ", sortedWords);
23    }
24}
25
1class Solution {
2public:
3    string sortSentence(string s) {
4        // Create input string stream to parse words from the sentence
5        istringstream inputStream(s);
6        string word;
7        vector<string> words;
8      
9        // Extract all words from the sentence (each word has a digit at the end)
10        while (inputStream >> word) {
11            words.push_back(word);
12        }
13      
14        // Create result vector with the same size as number of words
15        vector<string> sortedWords(words.size());
16      
17        // Place each word in its correct position based on the digit at the end
18        for (const auto& currentWord : words) {
19            // Extract position from last character (digit) and convert to 0-based index
20            int position = currentWord.back() - '1';
21          
22            // Remove the digit from the word and place it in correct position
23            sortedWords[position] = currentWord.substr(0, currentWord.size() - 1);
24        }
25      
26        // Build the result string by concatenating sorted words with spaces
27        string result;
28        for (const auto& word : sortedWords) {
29            result += word + " ";
30        }
31      
32        // Remove the trailing space
33        result.pop_back();
34      
35        return result;
36    }
37};
38
1/**
2 * Sorts words in a sentence based on the numeric suffix of each word
3 * @param s - Input string with words ending in position numbers (1-9)
4 * @returns The sentence with words sorted by their position numbers
5 */
6function sortSentence(s: string): string {
7    // Split the input string into individual words
8    const words: string[] = s.split(' ');
9  
10    // Initialize result array with the same length as words array
11    const sortedWords: string[] = new Array(words.length);
12  
13    // Process each word in the input
14    for (const word of words) {
15        // Extract the position index from the last character (convert from 1-based to 0-based)
16        // charCodeAt gets ASCII value, subtracting '1' ASCII value gives numeric position
17        const positionIndex: number = word.charCodeAt(word.length - 1) - '1'.charCodeAt(0);
18      
19        // Remove the last character (number) from the word and place it at correct position
20        sortedWords[positionIndex] = word.slice(0, -1);
21    }
22  
23    // Join the sorted words back into a sentence with spaces
24    return sortedWords.join(' ');
25}
26

Time and Space Complexity

The time complexity is O(n), where n is the length of the string s. This is because:

  • Splitting the string takes O(n) time to traverse through all characters
  • The for loop iterates through each word once, and each word's operations (accessing last character, slicing, and assignment) are O(1) on average
  • Joining the result array back into a string requires O(n) time to concatenate all characters

The space complexity is O(n), where n is the length of the string s. This is because:

  • The ws list stores all words from the split operation, which collectively contain all characters from s, requiring O(n) space
  • The ans list stores the reordered words (without their position digits), which is also O(n) space
  • The final joined string is O(n) space

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

Common Pitfalls

1. Assuming Words Can Have Multi-Digit Position Numbers

A common mistake is overthinking the problem and trying to handle position numbers with multiple digits (like "word10" or "word123"). This leads to unnecessarily complex parsing logic.

Incorrect approach:

# Trying to extract multi-digit numbers from the end
import re
def sortSentence(self, s: str) -> str:
    words = s.split()
    result = [None] * len(words)
  
    for word in words:
        # Overly complex regex to find trailing digits
        match = re.search(r'(\d+)$', word)
        if match:
            position = int(match.group(1))
            clean_word = word[:match.start()]
            result[position - 1] = clean_word
  
    return " ".join(result)

Why this is a pitfall: The problem explicitly states the sentence contains "no more than 9 words," meaning position numbers are always single digits (1-9). The simple word[-1] approach is sufficient and more efficient.

2. Forgetting to Convert from 1-Indexed to 0-Indexed

Another frequent error is directly using the position number as an array index without adjusting for 0-based indexing.

Incorrect approach:

def sortSentence(self, s: str) -> str:
    words = s.split()
    result = [None] * len(words)
  
    for word in words:
        position = int(word[-1])  # This is 1-indexed
        result[position] = word[:-1]  # ERROR: array index out of bounds
  
    return " ".join(result)

Why this fails: If we have 4 words with positions 1-4, trying to access result[4] will cause an IndexError since valid indices are 0-3.

Correct approach: Always subtract 1 from the position number: result[int(word[-1]) - 1] = word[:-1]

3. Not Handling Edge Cases with Word Content

Some might worry about words that naturally end with digits (though the problem doesn't include such cases).

Overthinking example:

def sortSentence(self, s: str) -> str:
    words = s.split()
    result = [None] * len(words)
  
    for word in words:
        # Trying to validate if the last character is "really" a position
        if word[-1].isdigit() and 1 <= int(word[-1]) <= 9:
            position = int(word[-1]) - 1
            result[position] = word[:-1]
        else:
            # Unnecessary error handling
            raise ValueError("Invalid word format")
  
    return " ".join(result)

Why this is unnecessary: The problem guarantees that each word has exactly one position digit at the end. Adding validation for cases that won't occur adds complexity without value.

Best practice: Trust the problem constraints and keep the solution simple and direct.

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

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

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

Load More