2108. Find First Palindromic String in the Array
Problem Description
You are given an array of strings called words
. Your task is to find and return the first string in this array that is a palindrome. If no palindromic string exists in the array, return an empty string ""
.
A palindrome is a string that reads exactly the same when read forwards or backwards. For example, "aba"
is a palindrome because reading it from left to right gives "aba" and reading it from right to left also gives "aba".
The key requirements are:
- Check each string in the array in the order they appear
- Return the first string that is a palindrome
- If none of the strings are palindromes, return
""
For example:
- If
words = ["abc", "car", "ada", "racecar", "cool"]
, the function should return"ada"
because it's the first palindrome in the array - If
words = ["def", "ghi"]
, the function should return""
because there are no palindromes
The solution uses Python's string slicing w[::-1]
to reverse each string and compare it with the original. The next()
function with a generator expression efficiently finds the first palindrome, returning ""
as the default value if no palindrome is found.
Intuition
Since we need to find the first palindromic string in the array, the most straightforward approach is to check each string one by one in the order they appear. As soon as we find a palindrome, we can immediately return it without checking the remaining strings.
To check if a string is a palindrome, we need to verify if it reads the same forwards and backwards. The simplest way to do this is to reverse the string and compare it with the original. If they match, we have found a palindrome.
In Python, we can reverse a string using slicing notation w[::-1]
. This creates a new string with characters in reverse order. By comparing w == w[::-1]
, we can instantly determine if w
is a palindrome.
The solution elegantly combines these ideas using Python's next()
function with a generator expression. The generator (w for w in words if w == w[::-1])
produces palindromic strings one at a time as it iterates through the array. The next()
function returns the first value from this generator, which is exactly what we want - the first palindrome. If the generator is exhausted (no palindromes found), next()
returns the default value we provide, which is the empty string ""
.
This approach is efficient because it stops as soon as the first palindrome is found, avoiding unnecessary checks on the remaining strings. It's also concise and readable, expressing the solution logic in a single line of code.
Learn more about Two Pointers patterns.
Solution Approach
We iterate through the array words
and check each string to determine if it's a palindrome. The first palindrome we encounter is returned immediately; if no palindromes exist, we return an empty string.
Step-by-step implementation:
-
Iterate through the array: We process each string
w
in thewords
array in order. -
Check for palindrome: For each string
w
, we compare it with its reversew[::-1]
. The slicing notation[::-1]
creates a reversed copy of the string by using a step of -1 to traverse the string backwards. -
Return first match: As soon as we find a string where
w == w[::-1]
evaluates toTrue
, we return that string. -
Handle no palindrome case: If we complete the iteration without finding any palindrome, we return an empty string
""
.
The solution uses Python's next()
function with a generator expression for a concise implementation:
- The generator expression
(w for w in words if w == w[::-1])
creates an iterator that yields only the palindromic strings from the array next()
retrieves the first value from this generator- The second parameter
""
serves as the default value returned when the generator is empty (no palindromes found)
Alternative approach using two pointers:
As mentioned in the reference, we could also check if a string is a palindrome using two pointers - one starting from the beginning and one from the end, moving towards the center. We compare characters at corresponding positions. If all pairs match, the string is a palindrome. However, the string reversal approach using w[::-1]
is more concise and equally efficient for this problem.
Time Complexity: O(n * m)
where n
is the number of strings and m
is the average length of the strings (in the worst case, we check all strings).
Space Complexity: O(m)
for creating the reversed string during comparison.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the example words = ["abc", "car", "ada", "racecar", "cool"]
.
Step 1: Check "abc"
- Original string: "abc"
- Reversed string: "cba" (using
"abc"[::-1]
) - Compare: "abc" == "cba" β False
- Not a palindrome, continue to next string
Step 2: Check "car"
- Original string: "car"
- Reversed string: "rac" (using
"car"[::-1]
) - Compare: "car" == "rac" β False
- Not a palindrome, continue to next string
Step 3: Check "ada"
- Original string: "ada"
- Reversed string: "ada" (using
"ada"[::-1]
) - Compare: "ada" == "ada" β True
- Found a palindrome! Return "ada" immediately
The function returns "ada" without checking the remaining strings "racecar" and "cool", even though "racecar" is also a palindrome. This demonstrates the efficiency of returning the first match.
Example with no palindromes: words = ["def", "ghi"]
Step 1: Check "def"
- Original string: "def"
- Reversed string: "fed"
- Compare: "def" == "fed" β False
- Not a palindrome, continue
Step 2: Check "ghi"
- Original string: "ghi"
- Reversed string: "ihg"
- Compare: "ghi" == "ihg" β False
- Not a palindrome, continue
Step 3: No more strings
- Reached the end of the array without finding any palindrome
- Return the default value: ""
Using the next()
function with generator expression, this entire process happens in one line:
return next((w for w in words if w == w[::-1]), "")
The generator yields "ada" as the first palindrome, and next()
returns it immediately.
Solution Implementation
1class Solution:
2 def firstPalindrome(self, words: List[str]) -> str:
3 """
4 Find the first palindrome string in the given list of words.
5
6 Args:
7 words: List of strings to check for palindromes
8
9 Returns:
10 The first palindrome found, or empty string if none exists
11 """
12 # Iterate through each word in the list
13 # Check if the word is equal to its reverse (palindrome check)
14 # Return the first palindrome found using generator expression with next()
15 # If no palindrome is found, return empty string as default
16 return next((word for word in words if word == word[::-1]), "")
17
1class Solution {
2 /**
3 * Finds the first palindrome string in the given array.
4 * A palindrome reads the same forwards and backwards.
5 *
6 * @param words Array of strings to search through
7 * @return The first palindrome found, or empty string if none exists
8 */
9 public String firstPalindrome(String[] words) {
10 // Iterate through each word in the array
11 for (String word : words) {
12 // Check if current word is a palindrome
13 if (isPalindrome(word)) {
14 return word;
15 }
16 }
17
18 // No palindrome found, return empty string
19 return "";
20 }
21
22 /**
23 * Helper method to check if a string is a palindrome.
24 * Uses two-pointer technique to compare characters from both ends.
25 *
26 * @param word The string to check
27 * @return true if the string is a palindrome, false otherwise
28 */
29 private boolean isPalindrome(String word) {
30 int left = 0;
31 int right = word.length() - 1;
32
33 // Compare characters from both ends moving towards center
34 while (left < right) {
35 if (word.charAt(left) != word.charAt(right)) {
36 return false;
37 }
38 left++;
39 right--;
40 }
41
42 return true;
43 }
44}
45
1class Solution {
2public:
3 string firstPalindrome(vector<string>& words) {
4 // Iterate through each word in the array
5 for (const auto& word : words) {
6 // Check if current word is a palindrome
7 if (isPalindrome(word)) {
8 return word; // Return the first palindrome found
9 }
10 }
11
12 // No palindrome found, return empty string
13 return "";
14 }
15
16private:
17 // Helper function to check if a string is a palindrome
18 bool isPalindrome(const string& str) {
19 int left = 0;
20 int right = str.size() - 1;
21
22 // Compare characters from both ends moving towards center
23 while (left < right) {
24 if (str[left] != str[right]) {
25 return false; // Characters don't match, not a palindrome
26 }
27 left++;
28 right--;
29 }
30
31 return true; // All characters matched, string is a palindrome
32 }
33};
34
1/**
2 * Finds the first palindrome string in an array of words
3 * @param words - Array of strings to search through
4 * @returns The first palindrome found, or empty string if none exists
5 */
6function firstPalindrome(words: string[]): string {
7 // Iterate through words array and find the first word that reads the same forwards and backwards
8 const palindrome = words.find((word: string) => {
9 // Check if the word is equal to its reversed version
10 // Split into characters, reverse the array, then join back into a string
11 return word === word.split('').reverse().join('');
12 });
13
14 // Return the found palindrome or empty string if no palindrome exists
15 return palindrome || '';
16}
17
Time and Space Complexity
The time complexity is O(L)
, where L
is the sum of the lengths of all strings in the array words
. This is because:
- In the worst case, we need to check every string in the array to find a palindrome (or determine none exists)
- For each string
w
, checking if it's a palindrome by comparingw == w[::-1]
takesO(len(w))
time - The string reversal
w[::-1]
creates a new string and compares it character by character - Therefore, the total time is the sum of lengths of all strings checked
The space complexity is O(1)
if we consider the space used for the output as not counting toward space complexity. The generator expression (w for w in words if w == w[::-1])
doesn't create intermediate storage for all elements, it processes them one at a time. The temporary reversed string w[::-1]
for each word uses O(m)
space where m
is the length of the current word being checked, but this is reused for each word and doesn't accumulate.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not handling empty strings correctly
Empty strings in the input array are technically palindromes (they read the same forwards and backwards). If you have specific requirements about whether to treat empty strings as valid palindromes, this needs explicit handling.
Example pitfall:
words = ["abc", "", "def"] # Returns "" which might be ambiguous
Solution:
def firstPalindrome(self, words: List[str]) -> str:
# Skip empty strings if they shouldn't be considered valid palindromes
return next((word for word in words if word and word == word[::-1]), "")
2. Case sensitivity issues
The current solution is case-sensitive, meaning "Aba" would not be recognized as a palindrome. Depending on requirements, this might be incorrect behavior.
Example pitfall:
words = ["Racecar", "hello"] # Returns "" even though "Racecar" is a palindrome ignoring case
Solution:
def firstPalindrome(self, words: List[str]) -> str:
# Case-insensitive palindrome check
return next((word for word in words if word.lower() == word.lower()[::-1]), "")
3. Inefficient string reversal for very long strings
While word[::-1]
is concise, it creates a complete reversed copy of the string in memory. For very long strings, this could be memory-intensive.
Solution using two-pointer approach:
def firstPalindrome(self, words: List[str]) -> str:
def isPalindrome(s):
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
for word in words:
if isPalindrome(word):
return word
return ""
4. Not handling None values in the array
If the input array might contain None values mixed with strings, the solution would crash with an AttributeError.
Example pitfall:
words = ["abc", None, "aba"] # Would raise AttributeError on None
Solution:
def firstPalindrome(self, words: List[str]) -> str:
return next((word for word in words if word is not None and word == word[::-1]), "")
What is the best way of checking if an element exists in a sorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!