Problem overview
The method isPalindrome receives a string, input, and returns true when it reads the same forwards and backwards, and false otherwise. The comparison is exact: every character counts, including spaces, and uppercase and lowercase letters are different characters.
In the first example, "madam racecar madam", the first and last characters are both m, the second and second-to-last are both a, and so on, all the way to the space in the middle. The answer is true. In the second, "abcbx", the first character is a and the last is x, so the answer is false.
The statement asks for O(n) time and O(1) extra space.
Approach: compare from both ends toward the middle
A string is a palindrome when the character at position k equals the character at position n - 1 - k, for every k. Each pair needs checking once, and the middle of the string pairs with itself.
A tempting shortcut is to build the reversed string and compare it with the original. That works, but it creates a second string of length n, which costs O(n) extra space. The statement asks for constant extra space, so the check should work directly on the original string.
The way to do it is with two pointers. One starts at the first character and the other at the last. While they haven't met, compare the two characters. If they differ, the string can't be a palindrome and the method can stop. If they match, move both pointers one step toward the middle and repeat.
Here is "abcba":
left = 0, right = 4: 'a' and 'a' match
left = 1, right = 3: 'b' and 'b' match
left = 2, right = 2: the pointers met, the loop stops
Every pair matched, so the result is true. The middle character, c, is never compared. That is fine, because it is its own mirror image.
Step by step
- Create the left pointer, a
numbernamedleft, starting at 0, the first index. It changes in the loop, so it is declared withlet. - Create the right pointer,
right, starting atinput.length - 1, the last index. Indices start at 0, so the last one is one less than the length. - Loop while
left < right. As long as the pointers haven't met or crossed, there is a pair left to compare. For an odd length, they meet on the middle character and the loop ends. - Compare the pair. If
input[left] !== input[right], returnfalseright away. One mismatched pair is enough to decide. - Move the pointers: increase
leftand decreaseright. Without this, the loop would compare the same pair forever. - Return
trueafter the loop. This line is reached only when every pair matched.
The solution in TypeScript
class Solution {
isPalindrome(input: string): boolean {
let left: number = 0;
let right: number = input.length - 1;
while (left < right) {
if (input[left] !== input[right]) {
return false;
}
left++;
right--;
}
return true;
}
}
The TypeScript details worth knowing:
- Indexing a string.
input[left]gives a one-character string. Strings are read-only: you can readinput[0], but you can't assign to it. That is why the code reads from the original instead of rearranging it. !==is strict inequality. It compares without converting types, and is the form to use in TypeScript. With two strings it is true when their contents differ.- The annotations
numberandstring. They are checked by the compiler and removed when the code is compiled to JavaScript. They don't change what runs. - UTF-16 units. A string's indices count UTF-16 code units, and some characters, such as many emoji, take two. For plain text like the examples this makes no difference, but such characters would be split into halves by the pointer approach.
Complexity
Time: O(n). Each pass of the loop moves the pointers two positions closer, so there are at most n / 2 passes, and each does constant work.
Space: O(1). The method keeps two numbers, no matter how long the string is.
Edge cases and common mistakes
- Empty string.
""hasright = -1, so the loop never runs and the result istrue. An empty string reads the same in both directions. - One character.
"a"starts withleft = right = 0, so no pair is compared and the result istrue. - Case and spaces count.
"Madam"givesfalse, becauseMandmare different characters."ab ba"givestrue, with the space as the middle character. The method doesn't ignore punctuation or spaces. - Using
left <= right. It would also compare the middle character with itself, which always matches. The result stays right, but the extra pass is not needed. - Forgetting to move the pointers. The loop would then never end, as long as the first pair matches.
- Comparing a reversed copy. It gives the right answer but uses
O(n)space, which the statement rules out.
Testing
"madam racecar madam"givestrue."abcbx"givesfalse.""givestrue."a"givestrue."Madam"givesfalse."ab ba"givestrue.