Problem overview
The function palindromeWords receives an array of words and returns the ones that are palindromes, in the same order. A palindrome reads the same from left to right and from right to left, like "noon". A one-letter word is a palindrome. If a palindrome appears twice in the input it is kept twice, and if none is a palindrome the result is empty. Built-in functions that reverse a word are off limits.
For ["pup", "cat", "noon", "dog"] the answer is ["pup", "noon"]. For ["a", "bob", "abba", "ab"] it is ["a", "bob", "abba"]. For ["kitten", "parrot"] it is [].
Approach: a helper for one word
The lesson splits the job in two. The helper isPalindrome answers the question for a single word, and palindromeWords keeps the words for which the answer is true.
To test one word, compare its first letter with its last, its second with the second to last, and so on. If any pair differs, the word is not a palindrome. Only half the word needs checking, because the second half repeats the same pairs. In a five-letter word the middle letter has no partner and needs no check.
Step by step
- Turn the word into an array of letters:
let letters = Array(word). A Swift string can't be read with a number index, but an array of its characters can. - Store the count in
let n = letters.count. - Loop over the first half:
for i in 0..<n / 2. Integer division drops the remainder, so 5 letters give 2 pairs and 4 letters give 2 pairs. - Compare the pair. The partner of
letters[i]isletters[n - 1 - i]. Fori = 0it is the last letter. If they differ, returnfalseat once, since one different pair settles it. - Return
trueafter the loop. A one-letter word skips the loop (0 pairs) and also gets here. - In
palindromeWords, start with an empty[String]namedpalindromes, declared withvar. - Loop over the words from first to last, so the order is kept.
- Append the word when
isPalindrome(word)is true. The call is already aBool, so it goes straight into theif. - Return
palindromes.
The solution in Swift
class Solution {
func isPalindrome(_ word: String) -> Bool {
let letters = Array(word)
let n = letters.count
for i in 0..<n / 2 {
if letters[i] != letters[n - 1 - i] {
return false
}
}
return true
}
func palindromeWords(_ words: [String]) -> [String] {
var palindromes = [String]()
for word in words {
if isPalindrome(word) {
palindromes.append(word)
}
}
return palindromes
}
}
Swift details: Array(word) gives an [Character], and two Character values compare with !=. In 0..<n / 2, the division happens before the range is formed, so the range is 0..<(n / 2). A range like 0..<0 is empty and the loop body never runs, which is what makes the one-letter case work.
Tracing the pairs
For "noon", n is 4 and the loop runs for i equal to 0 and 1. Index 0 pairs with index 3 (both n), and index 1 pairs with index 2 (both o). Every pair matches, so the helper returns true.
For "abca" the pair at indexes 0 and 3 matches (a and a), but the pair at indexes 1 and 2 is b and c. The helper returns false there, without looking further.
Why a helper is worth it
The pair-comparing loop is the hard part, and it lives in one place. palindromeWords is a plain filter: loop, ask, keep. You can try the helper alone on single words before trusting it inside the loop, and the second function never needs to know how the check works.
The three examples give:
["pup", "noon"]
["a", "bob", "abba"]
[]
Single checks: isPalindrome("a") is true, isPalindrome("ab") is false, isPalindrome("abc") is false and isPalindrome("abca") is false. The last one has matching ends but different middle letters, so it shows why every pair must be checked.
Another way, once built-ins are allowed
The lesson asks for the pair-comparing loop, but in a program of your own a palindrome test can also be written as word == String(word.reversed()), which builds the reversed word and compares the two. The filtering function can pass the helper straight to filter: words.filter { isPalindrome($0) } returns ["pup", "noon"] for the first example. Both forms give the same answers as the loops above. The loops show what is happening, and the pair comparison builds no second string.
Case and letters
The lesson's words are all lowercase, so no case handling is needed. In general, Swift compares characters exactly: isPalindrome("Noon") is false, because the capital N and the lowercase n are different characters, while isPalindrome("noon") is true.
Words that repeat
The constraints allow the same word to appear more than once. The loop treats every position separately, so ["noon", "noon"] returns both copies. The function does not remove repeats.
Common mistakes
- Looping over the whole word. It still gives the right answer, since the second half repeats the pairs, but it does twice the work.
- Returning
trueinside the loop after one matching pair. One match proves nothing. Only a different pair is conclusive. - Using
n - ias the partner index. The last index isn - 1, son - iruns off the end of the array and crashes the program. - Indexing the string directly.
word[0]does not compile in Swift, henceArray(word).
