Problem overview
IsPalindrome takes a string input and returns a bool: true when the string reads the same forwards and backwards, false otherwise. The statement asks for O(n) time and O(1) extra space.
"madam racecar madam" is a palindrome. The characters are compared one by one, including the spaces, and the first and last are both m, the second and second-to-last are both a, and so on. "abcbx" is not: its first character is a but its last is x.
Approach
The obvious solution reverses the string and compares it with the original. That works and is O(n) in time, but it builds a second string of n characters, so the extra space is O(n). The lesson asks for constant space, which rules it out.
A string is a palindrome exactly when each character equals its mirror image: input[0] with input[n - 1], input[1] with input[n - 2], and so on. Two index variables can walk toward each other to check all those pairs in one pass without copying anything. left starts at the first index and right at the last. While left < right, compare the two characters. A mismatch settles the answer at once: the string is not a palindrome. A match means both pointers step inward.
The loop stops when the pointers meet or cross. If the length is odd, the middle character has nothing to be compared with, which is fine because it is its own mirror. Reaching the end of the loop without a mismatch means every pair matched.
Tracing a failure
Take "abcbx", which has length 5. The pointers start at left = 0 and right = 4, and input[0] is a while input[4] is x. They differ, so the function returns false on the first comparison, without looking at the rest. Compare "abcba": the first pair a and a matches, the pointers move to 1 and 3, b matches b, and they move to 2 and 2. Now left < right is false, so the loop ends and the function returns true. The middle c is never compared, and it doesn't need to be.
The number of comparisons is at most half the length, rounded down. A string of 1,000 characters needs 500 checks at most, and a string with a mismatch at the ends needs one.
Step by step
- Create
leftas anintset to 0, the first index. - Create
rightas anintset toinput.Length - 1, the last index. Both must be variables because both change. - Open a
whileloop with the conditionleft < right. While it holds, there is still an unchecked pair. - Compare the pair. If
input[left] != input[right], returnfalseimmediately. - Move the pointers inward with
left++andright--. Without this the loop would check the same pair forever. - Return
trueafter the loop, since every pair matched.
The solution in C#
class Solution {
public bool IsPalindrome(string input) {
int left = 0;
int right = input.Length - 1;
while (left < right) {
if (input[left] != input[right]) {
return false;
}
left++;
right--;
}
return true;
}
}
C# details:
- Indexing a string gives a
char.input[left]is a single UTF-16 character, and!=on twocharvalues compares them as characters. No conversion is needed, and no new string is created, which keeps the extra space constant. - Read-only access. Strings are immutable in C#, so you can read
input[i]but never assign to it. That is no problem here because the algorithm only reads. bool. The return type isbool, with the literalstrueandfalsein lowercase.- Length is a property. A string uses
Length, not a method call:input.Length, notinput.Length().
C# char values are UTF-16 code units. For ordinary text this is exactly what you want. Characters outside the basic range, such as many emoji, take two code units, so a string like two identical emoji would be judged by code units rather than by visible characters. That doesn't matter for this lesson.
Complexity
Time: O(n). Each iteration moves the pointers one step closer, so the loop runs at most n/2 times, and each iteration does constant work. The early return can only make it faster.
Space: O(1). The function stores two integers, whatever the length of the input.
Edge cases and common mistakes
- The empty string.
rightis -1, the loop condition is false at once, and the function returnstrue. An empty string reads the same in both directions. - One character.
leftandrightare both 0, so the loop doesn't run and the answer istrue. - Using
left <= right. It also works, because it only adds a comparison of the middle character with itself. It is unnecessary though, so<is the usual choice. - Case and spaces count.
"Aa"returnsfalsebecause'A'and'a'are different characters, and"ab ba"is a palindrome only because of its middle space. Ignoring case or punctuation would be a different problem. - Forgetting to move a pointer. If only one of them moves, the pairs are no longer mirror images, and the answer is wrong.
- Using
input.Lengthas the last index. The last valid index isinput.Length - 1. Starting at the length throwsIndexOutOfRangeException.
Testing
"madam racecar madam"givestrue."abcbx"givesfalse.""and"a"givetrue."Aa"givesfalse.