Problem overview
The function palindromeWords gets a vector of words and returns the words that are palindromes, in the same order. A palindrome reads the same from left to right and from right to left, like "noon". A word with one letter is a palindrome. A word that appears twice and is a palindrome is kept twice. If no word is a palindrome, the result is empty. Every word has 1 to 20 lowercase letters.
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 an empty vector. The tests print a vector as its words separated by spaces.
The work is split between two functions. The helper isPalindrome answers the question for one word. palindromeWords keeps the words for which the helper says true. The helper compares letters from both ends in one loop, without a function that reverses the word.
Approach
Compare the first letter with the last, the second with the second to last, and so on. The first pair that differs proves the word isn't a palindrome, so the helper returns false immediately. If no pair differs, the word is a palindrome.
Only half of the word needs checking, because each turn of the loop covers two letters. For n letters the loop runs while i < n / 2. Integer division drops the remainder, so a 5-letter word makes 2 comparisons and its middle letter, which has no partner, is never looked at. A 1-letter word makes 0 comparisons and goes straight to true.
The partner of index i is n - 1 - i. For i = 0 that is the last index, n - 1; for i = 1 it is the index before that.
Watching the pairs
Here are the pairs the loop compares for three words:
nooncomparesnwithn, thenowitho. No pair differs, so it is a palindrome.catcomparescwithtand stops at once. They differ, so the word is not a palindrome, and theain the middle is never looked at.abcbacomparesawithaandbwithb. The middlechas no partner, and the word is a palindrome.
Step by step
- In
isPalindrome, store the lengthword.size()in anintnamedn. - Loop with
ifrom 0 whilei < n / 2. - Compare
word[i]withword[n - 1 - i]. If they differ, returnfalseright away. - After the loop, return
true. No pair differed. - In
palindromeWords, start with an emptyvector<string>namedpalindromes. - Loop over
wordswith astringnamedword. Going from first to last keeps the order. - If
isPalindrome(word)is true, add the word withpush_back. The helper's answer is already abool, so it goes straight into theif. - Return
palindromesafter the loop. It stays empty if nothing matched.
The solution in C++
class Solution {
public:
bool isPalindrome(string word) {
int n = word.size();
for (int i = 0; i < n / 2; i++) {
if (word[i] != word[n - 1 - i]) {
return false;
}
}
return true;
}
vector<string> palindromeWords(vector<string> &words) {
vector<string> palindromes;
for (string word : words) {
if (isPalindrome(word)) {
palindromes.push_back(word);
}
}
return palindromes;
}
};
Notes on the C++:
word.size()is unsigned. Storing it in anintfirst keepsn - 1 - ia normal signed calculation. With an unsignedn, a subtraction that goes below zero would wrap around to a huge number.word[i]is achar, and!=compares two characters by value.- The helper takes the word by value, so it works on a copy. The words here are at most 20 letters long.
Why the helper returns early
The helper is a yes or no question, and a single wrong pair answers it. Returning at that moment saves the remaining comparisons, and it keeps the code short: there is no flag to set and no extra variable. The return true after the loop is only reached when the loop ended without finding a mismatch.
Complexity
The helper makes at most half as many comparisons as the word has letters, and it often stops sooner. The main function calls it once per word, so the total work is proportional to the total number of letters in all the words. The result vector holds at most one copy of each input word.
Edge cases and common mistakes
- One-letter words.
"a"skips the loop and returnstrue. - Odd lengths.
"abcba"makes two comparisons, and the middlecis left alone. - Looping to
n. It still gives correct answers, since the second half repeats the first half's comparisons, but it does twice the work. - Off-by-one on the partner.
word[n - i]is out of range fori = 0. The partner isn - 1 - i. - Returning
trueinside the loop. A matching pair proves nothing about the other pairs. Only a mismatch can decide early. - Dropping duplicates. A repeated palindrome is kept every time; the code never removes anything.
Testing
{"pup", "cat", "noon", "dog"}givespup noon.{"a", "bob", "abba", "ab"}givesa bob abba.{"kitten", "parrot"}gives an empty vector.- For the helper alone,
isPalindrome("a")is true,isPalindrome("ab")is false andisPalindrome("abcba")is true.
