Facebook Pixel

1816. Truncate Sentence

Problem Description

You are given a sentence s which is a string of words separated by single spaces, with no leading or trailing spaces. Each word contains only uppercase and lowercase English letters with no punctuation marks.

Your task is to truncate the sentence so that it contains only the first k words. You need to return the truncated sentence.

For example:

  • If s = "Hello World this is amazing" and k = 3, you should return "Hello World this" (the first 3 words)
  • If s = "What is the solution to this problem" and k = 4, you should return "What is the solution to" (the first 4 words)

The solution approach splits the sentence into individual words using the split() method, takes the first k words using slice notation [:k], and then joins them back together with spaces using ' '.join(). This creates the truncated sentence containing exactly k words.

An alternative approach would be to iterate through the string character by character, counting spaces to track the number of words encountered. When you've found k-1 spaces (which means you've passed k words), you can return the substring up to that point. If the entire string is traversed without finding k words, the original string is returned.

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

Intuition

When we need to extract the first k words from a sentence, the most natural approach is to think about how words are structured in the sentence - they're separated by spaces. This immediately suggests two possible strategies:

The first thought is to leverage Python's built-in string manipulation capabilities. Since words are space-separated, we can use split() to break the sentence into a list of individual words. Once we have this list, getting the first k words is straightforward - we just slice the list with [:k]. Finally, we need to reconstruct the sentence by joining these words back with spaces.

The second approach comes from thinking about the problem more directly - if we want the first k words, we essentially need to find where the k-th word ends. Since words are separated by spaces, the k-th word ends right before the k-th space in the sentence. So we could traverse the string and count spaces. When we encounter the k-th space, we know we've found the boundary and can return everything before that position.

The split-and-join approach (' '.join(s.split()[:k])) is more elegant and concise because it directly expresses what we want to do: "take the first k words and put them back together." It also handles edge cases naturally - if there are fewer than k words in the sentence, split()[:k] will simply return all available words without any errors.

The space-counting approach, while more manual, can be slightly more efficient in terms of space complexity since it doesn't create an intermediate list of all words, but the difference is negligible for most practical purposes.

Solution Approach

The reference solution uses a simulation approach by traversing the string character by character and counting spaces to determine word boundaries.

Let's walk through the implementation:

  1. Initialize traversal: Start iterating through the string s from index 0.

  2. Count words by spaces: For each character s[i] we encounter:

    • If the character is a space, it means we've just completed reading a word
    • Decrement k by 1 since we've processed one word
    • When k reaches 0, we've found exactly k words
  3. Return the truncated string: When k becomes 0, return the substring s[0:i] which contains all characters from the beginning up to (but not including) the current space character. This gives us exactly the first k words.

  4. Handle edge case: If we traverse the entire string without k reaching 0, it means the sentence has fewer than k words. In this case, return the entire string s.

The algorithm works because:

  • Each space marks the end of a word (except there's no trailing space after the last word)
  • By counting spaces, we effectively count completed words
  • The position where we find the k-th space is exactly where we need to truncate

Time Complexity: O(n) where n is the length of the string, as we traverse the string once.

Space Complexity: O(1) for the traversal approach (not counting the output string), or O(n) for the split-and-join approach due to the intermediate list creation.

The provided solution code uses a more Pythonic approach with ' '.join(s.split()[:k]):

  • s.split() breaks the sentence into a list of words
  • [:k] slices the first k words from the list
  • ' '.join() concatenates them back with spaces between words

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 the solution with the example s = "Hello World this is amazing" and k = 3.

Using the split-and-join approach:

  1. Start with the sentence: "Hello World this is amazing"

  2. Apply s.split() to break it into words:

    • Result: ["Hello", "World", "this", "is", "amazing"]
  3. Take the first k = 3 words using slice notation [:3]:

    • Result: ["Hello", "World", "this"]
  4. Join these words back together with spaces using ' '.join():

    • Result: "Hello World this"

Using the space-counting approach:

  1. Start traversing the string from index 0, with k = 3

  2. Iterate through each character:

    • Index 0-4: "Hello" - no space yet
    • Index 5: Found first space → k = 2 (one word completed)
    • Index 6-10: "World" - continue
    • Index 11: Found second space → k = 1 (two words completed)
    • Index 12-15: "this" - continue
    • Index 16: Found third space → k = 0 (three words completed)
  3. When k reaches 0 at index 16, return s[0:16]

    • Result: "Hello World this"

Both approaches yield the same result. The first approach is more direct and Pythonic, while the second approach shows the underlying logic of identifying word boundaries through space counting.

Solution Implementation

1class Solution:
2    def truncateSentence(self, s: str, k: int) -> str:
3        """
4        Truncates a sentence to contain only the first k words.
5      
6        Args:
7            s: Input sentence as a string with words separated by spaces
8            k: Number of words to keep from the beginning of the sentence
9      
10        Returns:
11            A string containing only the first k words from the original sentence
12        """
13        # Split the sentence into a list of words using whitespace as delimiter
14        words = s.split()
15      
16        # Take only the first k words from the list
17        first_k_words = words[:k]
18      
19        # Join the selected words back into a string with spaces
20        truncated_sentence = ' '.join(first_k_words)
21      
22        return truncated_sentence
23
1class Solution {
2    /**
3     * Truncates a sentence to contain only the first k words.
4     * 
5     * @param s The input sentence with words separated by single spaces
6     * @param k The number of words to keep
7     * @return A string containing the first k words from the sentence
8     */
9    public String truncateSentence(String s, int k) {
10        // Iterate through each character in the string
11        for (int i = 0; i < s.length(); i++) {
12            // Check if current character is a space (word separator)
13            if (s.charAt(i) == ' ') {
14                // Decrement k and check if we've found k words
15                k--;
16                if (k == 0) {
17                    // Return substring from start to current position (excluding the space)
18                    return s.substring(0, i);
19                }
20            }
21        }
22      
23        // If we've gone through the entire string without finding k spaces,
24        // it means the sentence has exactly k words or fewer, so return the entire string
25        return s;
26    }
27}
28
1class Solution {
2public:
3    string truncateSentence(string s, int k) {
4        // Iterate through each character in the string
5        for (int i = 0; i < s.size(); ++i) {
6            // Check if current character is a space
7            if (s[i] == ' ') {
8                // Decrement k and check if we've found k words
9                k--;
10                if (k == 0) {
11                    // Return substring from start to current position (excluding the space)
12                    return s.substr(0, i);
13                }
14            }
15        }
16        // If we've processed the entire string without finding k spaces,
17        // it means the sentence has k or fewer words, so return the entire string
18        return s;
19    }
20};
21
1/**
2 * Truncates a sentence to contain only the first k words.
3 * Words are separated by single spaces.
4 * 
5 * @param s - The input sentence string
6 * @param k - The number of words to keep
7 * @returns The truncated sentence containing the first k words
8 */
9function truncateSentence(s: string, k: number): string {
10    // Iterate through each character in the string
11    for (let i = 0; i < s.length; i++) {
12        // Check if current character is a space
13        if (s[i] === ' ') {
14            // Decrement k and check if we've found k words
15            k--;
16            if (k === 0) {
17                // Return substring from start to current position (excluding the space)
18                return s.slice(0, i);
19            }
20        }
21    }
22  
23    // If we've gone through the entire string without finding k spaces,
24    // it means the sentence has k or fewer words, so return the entire string
25    return s;
26}
27

Time and Space Complexity

Time Complexity: O(n), where n is the length of the string s. The split() method needs to traverse the entire string to identify all word boundaries, which takes O(n) time. The slicing operation [:k] takes O(k) time, and the join() operation takes O(m) time where m is the total length of the first k words. Since m ≤ n, the overall time complexity is dominated by O(n).

Space Complexity: O(n). The split() method creates a new list containing all words from the string, which in the worst case (when the string contains no spaces) could require O(n) space. Additionally, the slicing [:k] creates another list of size k, and the join() creates the output string. If we exclude the space required for the output (as mentioned in the reference), we still have O(n) space complexity due to the intermediate list created by split().

Note: The reference answer states space complexity as O(1) when ignoring the output space, but this appears to be incorrect for this implementation. The split() method creates an intermediate list that requires O(n) auxiliary space.

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

Common Pitfalls

1. Assuming Multiple Spaces Between Words

A common mistake is thinking the input might have multiple consecutive spaces between words, leading to unnecessary complexity in the solution.

Pitfall Example:

# Overcomplicated approach trying to handle multiple spaces
def truncateSentence(self, s: str, k: int) -> str:
    words = []
    current_word = ""
    for char in s:
        if char == ' ':
            if current_word:  # Only add non-empty words
                words.append(current_word)
                current_word = ""
        else:
            current_word += char
    # Don't forget the last word!
    if current_word:
        words.append(current_word)
    return ' '.join(words[:k])

Solution: The problem guarantees single spaces between words with no leading or trailing spaces. Simply use s.split() which handles this perfectly.

2. Off-by-One Error in Manual Traversal

When implementing the character-by-character traversal approach, it's easy to miscount spaces or words.

Pitfall Example:

def truncateSentence(self, s: str, k: int) -> str:
    if k == 0:
        return ""
  
    space_count = 0
    for i in range(len(s)):
        if s[i] == ' ':
            space_count += 1
            if space_count == k:  # Wrong! This finds k spaces, not k words
                return s[:i]
    return s

Solution: Remember that k words means finding k-1 spaces (since there's no space after the last word). The correct condition should be if space_count == k - 1 or decrement k when finding spaces and check if k == 1.

3. Forgetting Edge Cases

Not handling cases where the sentence has fewer words than k.

Pitfall Example:

def truncateSentence(self, s: str, k: int) -> str:
    words = s.split()
    return ' '.join(words[:k])  # This actually works, but some might try...
  
    # Incorrect attempt at "optimization"
    if len(words) < k:
        raise ValueError("Not enough words")  # Wrong! Should return entire string

Solution: Python's list slicing words[:k] automatically handles this case correctly - if k is larger than the list length, it returns the entire list. No special handling needed.

4. Inefficient String Concatenation

Building the result character by character in a loop can be inefficient.

Pitfall Example:

def truncateSentence(self, s: str, k: int) -> str:
    result = ""
    word_count = 0
    current_word = ""
  
    for char in s:
        if char == ' ':
            if word_count < k:
                if result:
                    result += " " + current_word  # String concatenation in loop
                else:
                    result = current_word
                word_count += 1
                current_word = ""
        else:
            current_word += char  # More string concatenation
  
    # Handle last word
    if word_count < k and current_word:
        if result:
            result += " " + current_word
        else:
            result = current_word
  
    return result

Solution: Use split() and join() for cleaner, more efficient code. If manual traversal is needed, find the truncation index and use string slicing s[:index] instead of building character by character.

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

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

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

Load More