A palindrome is a string that reads the same forwards and backwards: "racecar", "level", "abba". Implement stringPalindrome(str) — return true if the input reads the same in both directions, false otherwise.
Treat the comparison as strict: case matters and every character counts. "Racecar" is not a palindrome (capital R differs from lowercase r), and "race car" is not a palindrome (the space at index 4 differs from the e at the mirror position). If the caller wants to normalize, they can do it before calling you.
function stringPalindrome(str) {
// returns true if str reads the same forwards and backwards,
// false otherwise.
}
stringPalindrome('racecar'); // true
stringPalindrome('level'); // true
stringPalindrome('hello'); // false
stringPalindrome(''); // true — empty string is trivially a palindrome
stringPalindrome('a'); // true — single char mirrors itself
stringPalindrome('Racecar'); // false — case-sensitive
stringPalindrome('race car'); // false — whitespace is significant
true. There is nothing to mismatch.true. It is its own mirror.'A' !== 'a'. Do not lowercase the input.' ' is just another character.You'll check whether a string is its own mirror by walking two pointers inward from opposite ends and comparing the characters they land on.
A palindrome is a string that reads the same forwards and backwards — "racecar", "abba", "a". Your job is to return true for those and false for anything else, like "hello". The comparison is strict: the same character, in the same case, at the same offset from each end.
The shape of the answer is small: a boolean. The interesting part is how you decide.
Picture two fingers on a printed string. One starts on the leftmost character, the other on the rightmost. Both move toward the middle one step at a time. At every step, you compare the two characters under your fingers. The moment they differ, the answer is false. If they meet (or cross) without ever differing, the answer is true.
The most direct way to express "reads the same forwards and backwards" in code is to literally reverse the string and check for equality:
function stringPalindromeNaive(str) {
// Spread into chars, reverse the array in place, join back to a string.
const reversed = [...str].reverse().join('');
return str === reversed;
}
This is correct — it returns the right answer for every input. But it pays a cost the question never asked for: it allocates a new array of every character, mutates it, and then allocates a new string. For "hello", where the mismatch is obvious from just the first and last characters ('h' vs 'o'), you've still walked the entire string three times before you knew the answer. The two-pointer version finds the same false after reading exactly two characters.
function stringPalindrome(str) {
// Two pointers, one at each end. We walk them toward the middle.
let left = 0;
let right = str.length - 1;
// Stop the moment they meet or cross — once left >= right, every pair
// has already been checked (or there's only the middle cell left, and
// a single character trivially mirrors itself).
while (left < right) {
// First mismatch is enough — no need to look at the rest.
if (str[left] !== str[right]) return false;
left++;
right--;
}
// Loop drained without a mismatch → every mirror pair matched.
return true;
}
module.exports = { stringPalindrome };
Three things to notice. First, the loop condition is left < right, not left <= right: when they're equal, both fingers are on the same character, and a character always equals itself — no work to do. Second, the function returns false inside the loop the instant it finds a mismatch; it does not finish scanning. Third, the empty string and a single-character string both skip the loop entirely (the initial left < right check is 0 < -1 and 0 < 0 respectively) and fall through to return true — exactly what we want.
Take stringPalindrome('racecar'). The string has length 7, so left = 0 and right = 6.
left=0, right=6. str[0] is 'r', str[6] is 'r'. They match. Increment left to 1, decrement right to 5.left=1, right=5. str[1] is 'a', str[5] is 'a'. They match. left=2, right=4.left=2, right=4. str[2] is 'c', str[4] is 'c'. They match. left=3, right=3.left < right is now 3 < 3, which is false. The loop exits. We fall through to return true.The middle character ('e' at index 3) was never compared to anything, and that's correct: it has no mirror, so there's nothing to check.
Now try a failing case, stringPalindrome('hello'). left = 0, right = 4.
str[0] is 'h', str[4] is 'o'. They differ. Return false immediately. Steps 2 and 3 never run.That early return is the whole reason the two-pointer walk is worth writing instead of str === str.split('').reverse().join('').
<= instead of < in the loop condition — while (left <= right) runs one extra iteration where left === right, comparing a character to itself. It still gives the right answer, but the comparison is wasted work. Worse, on the empty string left = 0 and right = -1, and 0 <= -1 is false, so it happens to work there too — but on a string of length 1, left=0, right=0, 0 <= 0 is true, and you'd index str[0] against str[0]. Harmless, but signals you haven't thought about the boundary. Use <.left (and forgetting right--) turns the walk into a linear scan from the front against the same fixed character at the end. "abc" would compare 'a' to 'c', then 'b' to 'c', then 'c' to 'c', and return true — wrong. Both pointers must move every iteration.stringPalindrome('Racecar') is false because 'R' !== 'r'. If you call str.toLowerCase() first you'd return true, which is the wrong answer for this function. A caller who wants a case-insensitive check can normalize before calling.str or the loop counter instead of a boolean — the function returns true or false, never the string itself, never 0 or 1. The test for shape (typeof is 'boolean') catches this immediately.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.
A palindrome is a string that reads the same forwards and backwards: "racecar", "level", "abba". Implement stringPalindrome(str) — return true if the input reads the same in both directions, false otherwise.
Treat the comparison as strict: case matters and every character counts. "Racecar" is not a palindrome (capital R differs from lowercase r), and "race car" is not a palindrome (the space at index 4 differs from the e at the mirror position). If the caller wants to normalize, they can do it before calling you.
function stringPalindrome(str) {
// returns true if str reads the same forwards and backwards,
// false otherwise.
}
stringPalindrome('racecar'); // true
stringPalindrome('level'); // true
stringPalindrome('hello'); // false
stringPalindrome(''); // true — empty string is trivially a palindrome
stringPalindrome('a'); // true — single char mirrors itself
stringPalindrome('Racecar'); // false — case-sensitive
stringPalindrome('race car'); // false — whitespace is significant
true. There is nothing to mismatch.true. It is its own mirror.'A' !== 'a'. Do not lowercase the input.' ' is just another character.You'll check whether a string is its own mirror by walking two pointers inward from opposite ends and comparing the characters they land on.
A palindrome is a string that reads the same forwards and backwards — "racecar", "abba", "a". Your job is to return true for those and false for anything else, like "hello". The comparison is strict: the same character, in the same case, at the same offset from each end.
The shape of the answer is small: a boolean. The interesting part is how you decide.
Picture two fingers on a printed string. One starts on the leftmost character, the other on the rightmost. Both move toward the middle one step at a time. At every step, you compare the two characters under your fingers. The moment they differ, the answer is false. If they meet (or cross) without ever differing, the answer is true.
The most direct way to express "reads the same forwards and backwards" in code is to literally reverse the string and check for equality:
function stringPalindromeNaive(str) {
// Spread into chars, reverse the array in place, join back to a string.
const reversed = [...str].reverse().join('');
return str === reversed;
}
This is correct — it returns the right answer for every input. But it pays a cost the question never asked for: it allocates a new array of every character, mutates it, and then allocates a new string. For "hello", where the mismatch is obvious from just the first and last characters ('h' vs 'o'), you've still walked the entire string three times before you knew the answer. The two-pointer version finds the same false after reading exactly two characters.
function stringPalindrome(str) {
// Two pointers, one at each end. We walk them toward the middle.
let left = 0;
let right = str.length - 1;
// Stop the moment they meet or cross — once left >= right, every pair
// has already been checked (or there's only the middle cell left, and
// a single character trivially mirrors itself).
while (left < right) {
// First mismatch is enough — no need to look at the rest.
if (str[left] !== str[right]) return false;
left++;
right--;
}
// Loop drained without a mismatch → every mirror pair matched.
return true;
}
module.exports = { stringPalindrome };
Three things to notice. First, the loop condition is left < right, not left <= right: when they're equal, both fingers are on the same character, and a character always equals itself — no work to do. Second, the function returns false inside the loop the instant it finds a mismatch; it does not finish scanning. Third, the empty string and a single-character string both skip the loop entirely (the initial left < right check is 0 < -1 and 0 < 0 respectively) and fall through to return true — exactly what we want.
Take stringPalindrome('racecar'). The string has length 7, so left = 0 and right = 6.
left=0, right=6. str[0] is 'r', str[6] is 'r'. They match. Increment left to 1, decrement right to 5.left=1, right=5. str[1] is 'a', str[5] is 'a'. They match. left=2, right=4.left=2, right=4. str[2] is 'c', str[4] is 'c'. They match. left=3, right=3.left < right is now 3 < 3, which is false. The loop exits. We fall through to return true.The middle character ('e' at index 3) was never compared to anything, and that's correct: it has no mirror, so there's nothing to check.
Now try a failing case, stringPalindrome('hello'). left = 0, right = 4.
str[0] is 'h', str[4] is 'o'. They differ. Return false immediately. Steps 2 and 3 never run.That early return is the whole reason the two-pointer walk is worth writing instead of str === str.split('').reverse().join('').
<= instead of < in the loop condition — while (left <= right) runs one extra iteration where left === right, comparing a character to itself. It still gives the right answer, but the comparison is wasted work. Worse, on the empty string left = 0 and right = -1, and 0 <= -1 is false, so it happens to work there too — but on a string of length 1, left=0, right=0, 0 <= 0 is true, and you'd index str[0] against str[0]. Harmless, but signals you haven't thought about the boundary. Use <.left (and forgetting right--) turns the walk into a linear scan from the front against the same fixed character at the end. "abc" would compare 'a' to 'c', then 'b' to 'c', then 'c' to 'c', and return true — wrong. Both pointers must move every iteration.stringPalindrome('Racecar') is false because 'R' !== 'r'. If you call str.toLowerCase() first you'd return true, which is the wrong answer for this function. A caller who wants a case-insensitive check can normalize before calling.str or the loop counter instead of a boolean — the function returns true or false, never the string itself, never 0 or 1. The test for shape (typeof is 'boolean') catches this immediately.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.