Skip to content
← All posts

Goat Latin: String Transformation Word by Word

4 min read
leetcodeproblemeasystrings

LeetCode 824, Goat Latin, asks you to transform a sentence one word at a time according to a small set of rules. Each word is modified based on whether it starts with a vowel or a consonant, and every word also picks up a trailing suffix that grows with its position in the sentence.

The rules are:

  • If a word begins with a vowel (a, e, i, o, u, case-insensitive), append "ma" to the end.
  • If a word begins with a consonant, move the first letter to the end of the word, then append "ma".
  • For the k-th word (1-indexed), append k copies of "a" after the "ma".

Given the input "I speak Goat Latin", the expected output is "Imaa peaksmaaa oatGmaaaa atinLmaaaaa".

originaltransformed#1IImaa#2speakpeaksmaaa#3GoatoatGmaaaa#4LatinatinLmaaaaavowel startconsonant (moved)"ma" + "a" suffix
The sentence "I speak Goat Latin" transformed word by word. Vowel-starting words keep their letters in place. Consonant-starting words move the first letter to the end. Every word gets "ma" plus an increasing number of "a"s appended.

The approach

You can solve this in a single pass through the words:

  1. Split the sentence on spaces to get individual words.
  2. Iterate over each word with its index.
  3. Check whether the first character is a vowel or consonant.
  4. Transform the word accordingly: leave it as-is for vowels, or rotate the first character to the end for consonants.
  5. Append "ma" followed by (index + 1) copies of "a".
  6. Join all transformed words back together with spaces.

The logic is entirely local to each word. No word depends on any other word's transformation. That makes the code clean and the reasoning simple.

Python solution

def toGoatLatin(sentence):
    vowels = set("aeiouAEIOU")
    words = sentence.split()
    result = []
    for i, word in enumerate(words):
        if word[0] in vowels:
            transformed = word + "ma"
        else:
            transformed = word[1:] + word[0] + "ma"
        transformed += "a" * (i + 1)
        result.append(transformed)
    return " ".join(result)

The vowel set includes both lowercase and uppercase letters so the check works regardless of case. The consonant branch slices off the first character with word[1:], appends the original first character at the end, then adds "ma". The "a" * (i + 1) expression generates the right number of trailing "a" characters since i is zero-indexed.

Step-by-step walkthrough

Here is the full transformation of "I speak Goat Latin", traced word by word.

Step 1: Split the sentence into words

"I speak Goat Latin" -> ["I", "speak", "Goat", "Latin"]

Split on spaces to get individual words. We process each word independently based on its position (1st, 2nd, 3rd, ...).

Step 2: Check the first character of each word

"I" starts with vowel | "speak" starts with consonant | "Goat" starts with consonant | "Latin" starts with consonant

Compare the first character (case-insensitive) against the set {a, e, i, o, u}. This determines which transformation rule to apply.

Step 3: Apply the transformation rule

"I" -> "I" + "ma" | "speak" -> "peaks" + "ma" | "Goat" -> "oatG" + "ma" | "Latin" -> "atinL" + "ma"

Vowel words: append "ma" directly. Consonant words: move the first letter to the end, then append "ma".

Step 4: Append "a" characters based on word position

"Ima" + "a" | "peaksma" + "aa" | "oatGma" + "aaa" | "atinLma" + "aaaa"

The 1st word gets one "a", the 2nd gets two, the 3rd gets three, and so on. The count matches the 1-indexed position of the word.

Step 5: Join the transformed words

"Imaa peaksmaaa oatGmaaaa atinLmaaaaa"

Rejoin all transformed words with a single space to produce the final output.

Each word is handled independently. The only thing that changes from one word to the next is the number of trailing "a" characters.

Complexity

Complexity
TimeO(n)
SpaceO(n)

Every character in the sentence is visited once during the split, once during the transformation, and once during the join. The suffix characters add at most O(k) work per word where k is the word index, but across all words the total suffix length is O(n) in the worst case (where n is the total length of the output). Space is O(n) for storing the result list and the final joined string.

Building blocks

This problem exercises two fundamental string skills that appear across many LeetCode problems.

Character classification

Checking whether a character belongs to a specific set (vowels, digits, letters) is a building block you will use constantly. Here it determines the transformation branch. The same idea shows up in problems like Valid Palindrome (filtering non-alphanumeric characters) and Find the Index of the First Occurrence in a String (character-by-character comparison). Building a quick lookup with a set keeps the check at O(1).

String rotation and concatenation

Moving the first character to the end is a minimal rotation. The slice-and-concatenate pattern word[1:] + word[0] is a technique that generalizes to rotating strings by any number of positions. You will see similar slicing logic in Reverse Words in a String (rearranging tokens) and Reverse String (reordering characters in place). Understanding how slicing maps to character positions makes these operations feel natural.

Edge cases

Single word, vowel start. "apple" becomes "applema" with one trailing "a", giving "applemaa". No rotation needed.

Single word, consonant start. "goat" becomes "oatgma" with one trailing "a", giving "oatgmaa". The "g" moves to the end before "ma" is appended.

All vowels. A sentence like "I eat oats" applies the vowel rule to every word. No character rotation happens at all.

Mixed case. "Each" starts with "E", which is a vowel. Your vowel check must be case-insensitive. Using a set that includes both cases (or calling .lower() on the first character) handles this.

From understanding to recall

The rules for Goat Latin are simple enough to remember after reading them once. But reproducing the code from scratch is a different challenge. The details that trip people up are small: remembering to check both cases for vowels, using 1-indexed counts for the trailing "a" characters, slicing correctly with word[1:] + word[0] instead of something off by one.

These details are exactly the kind of thing that fades after a few days. Spaced repetition targets that gap. Instead of re-reading the solution when you forget a detail, you reconstruct the code actively, which is what locks it into long-term memory. Each review takes less time than the last, and after a few rounds the whole solution becomes automatic.

Related posts

Goat Latin is a good warm-up for building fluency with string operations. The transformation rules are mechanical, but implementing them cleanly requires comfort with slicing, set lookups, and positional indexing. Once those pieces are solid, you can apply them to harder string problems without hesitation.