Skip to content
Skilled.dev

Free lesson walkthrough · Swift

Group palindrome permutations in Swift: counts and keys

Filter palindrome permutations, then group retained strings by character multiset. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

4 min read · Worked code, edge cases, and complexity

Keep only strings whose characters can be rearranged into a palindrome, then group the retained strings by their character multiset. For ['naa', 'aan', 'todo', 'doto', 'code'], only naa and aan qualify, so return one group containing those two strings. This task combines a palindrome-permutation filter with an anagram grouping step.

Test character-count parity

A palindrome pairs equal characters around its center. Every character count must therefore be even, except that one character may have an odd count and occupy the center. Count each character and then count how many character frequencies are odd. The string qualifies exactly when that number is at most one.

For naa, a appears twice and n appears once, so it qualifies. For todo, o appears twice while t and d each appear once. Two odd counts cannot share one center, so it fails. The original character order is irrelevant to this test; the question asks whether some rearrangement is a palindrome.

Group qualifying anagrams

Sort a qualifying string's characters to form a canonical key. Any two strings with the same character multiset produce the same sorted key, and different multisets produce different keys. Append the original string to the group stored under that key. Return the groups after processing every input string.

Both naa and aan produce key aan. A qualifying string such as bob produces a different key and belongs to another group. Keep the original strings in the output rather than their sorted keys. The keys are an internal lookup representation, not the requested answer.

Keep filtering separate from grouping

Ordinary anagram grouping would retain todo and doto together. This lesson must exclude that entire group because neither can form a palindrome. Grouping first and forgetting the parity condition passes many familiar anagram examples while answering the wrong problem.

An empty string has no odd counts and qualifies. A one-character string has one odd count and also qualifies. Duplicate input strings remain duplicate entries in their output group. The course comparator ignores group order and order within groups, but it still checks values and multiplicities. Replacing each group with a set would discard required occurrences.

Explain correctness through the key

The parity test is necessary because all noncentral positions come in equal pairs. It is sufficient because even counts can be divided equally between the two sides, with the optional odd character placed in the center. Thus the filter retains exactly the eligible strings.

For retained strings, equal sorted keys mean equal counts for every character, which is exactly the anagram relation. Appending each retained occurrence once preserves the input multiplicities. The result therefore partitions precisely the eligible input occurrences into the requested groups, without requiring an expensive generation of their possible palindromes.

Costs, text assumptions, and checks

For n strings of length at most c, parity counting costs O(nc), and sorting keys costs O(nc log c). Stored keys and grouped text require space proportional to the retained input and distinct keys. A fixed alphabet can use a count-vector key in O(c) per string, but its serialization must distinguish counts unambiguously.

The course uses lowercase alphanumeric text. General Unicode requires a defined unit and normalization policy before counting or sorting; byte order and displayed characters are different concepts. Test zero, one, and two odd counts, empty input, empty strings, duplicate strings, several eligible anagram groups, and ineligible anagrams. An independent oracle can compare frequency maps directly and use the parity rule without copying the implementation's sorted-key construction.

Swift implementation

The dictionary’s default empty array lets a qualifying string append directly to its group. Sorting uses Swift Character ordering.

The course uses the class shown here and supplies any declared node classes. Swift arrays have value semantics with copy-on-write storage, while class instances such as linked nodes retain reference identity. Optional values require an explicit absence case. Keep the starter's argument labels when calling the method. Swift integer overflow traps, so expanded constraints require a checked arithmetic policy.

class Solution {
    func groupPermutationsOfPalindromes(_ strings: [String]) -> [[String]] {
        var permutationsOfPalindromes: [String: [String]] = [:]

        // Iterate through all O(n) strings in our array
        for string in strings {
            // Check if each string is a palindrome O(c)
            if isPermutationOfPalindrome(string) {
                // Sort our string's order O(c log c)
                // Permutations will sort to the same string
                let sortedString = String(string.sorted())

                // Add the string to its group, starting with an empty
                // array if we haven't seen any of the string's permutations yet
                permutationsOfPalindromes[sortedString, default: []].append(string)
            }
        }

        // This will return all the values of the hash table as an array
        // Since the values are also arrays, this is the form of the result we want
        return Array(permutationsOfPalindromes.values)
    }

    // We use the remainder operator to determine if a number is odd
    private func isOdd(_ num: Int) -> Bool {
        return num % 2 != 0
    }

    // All O(c) operations
    private func isPermutationOfPalindrome(_ string: String) -> Bool {
        // Use a hash table to count the occurrence of each character in a string
        // These are sequential O(c) operations
        var characterCountTable: [Character: Int] = [:]

        for char in string {
            characterCountTable[char, default: 0] += 1
        }

        // Sum the number of characters that contain an odd count
        var oddCharacterCount = 0
        for numCharacters in characterCountTable.values where isOdd(numCharacters) {
            oddCharacterCount += 1
        }

        // If only one character has an odd count, this function returns true
        return oddCharacterCount <= 1
    }
}

Practice this implementation

Open Group Permutations of Palindromes to run the implementation against the course tests, then change one boundary condition and explain the first failing case. The editor supplies the starter signature and any node types or helper APIs used above. Copy the implementation into that environment; the snippet is the solution body, without the input parser or test driver. Your saved progress belongs to the original lesson.

For more Swift conventions and practice problems, use the Swift interview guide.

Try the exercise

Write your own implementation in the course editor. This walkthrough belongs to the same lesson; your course progress stays with that exercise. Exercise access follows the course’s access settings.