So I was monkeying around, and I decided to write a function to determine if a string were a palindrome. This is not a very difficult problem, but it might make for a descent introductory interview question (one of those questions you'd ask to rule out the truly inept).
With problems like this, I'm mostly interested in two things:
- Writing the function in a clever way, so as to achieve the least amount of code possible
- Writing the function in the most efficient way possible
Now, if it were Ruby, writing the clever solution (which might also happen to be the most efficient solution in that language -- not sure) would be ridiculously easy:
s == s.reverse
That's so trivial, it's probably not even worth writing.
return s === s.split('').reverse().join('');
For grins and giggles, I decided to see how a manual solution, where I compare characters starting at each end of the string and work towards the middle, would compare:
var i, j,
len = s.length,
mid = Math.floor(len / 2);
for (i = 0, j = len - 1; i < mid; i++, j--)
if (s.charAt(i) !== s.charAt(j)) return false;
This function will probably fail quickly, so that's good, but it also turns out to be generally faster than the split/reverse/join version. It makes sense, since you're going through the string only one time and twice as fast as normal since you're traversing from both ends at the same time.
Can you do even better though? Maybe. At least in Firefox you can, and quick tests seemed to confirm across other browsers too (but I didn't test extensively).
What if we split the string in half, and only reverse half of it. Then we can compare the first half with the original half:
var len = s.length,
x = len / 2,
y = x === Math.floor(x) ? x : (x = Math.floor(x)) + 1;
return s.substr(0, x).split('').reverse().join('') === s.substr(y,len);
The idea here is just split the string in half, reverse one of the two pieces and compare them. For example:
TOOT: splits into "TO" and "OT." Reverse one of them, say the second part ("OT") and compare.
If you've got a odd number of characters, you can ignore the middle most character:
RACECAR: splits into "RAC" and "CAR" (ignore the "E" in the middle). Reverse the second part ("CAR") and compare.
As it turned out, this seemed to be faster than the other two methods. Not by orders of magnitude, but not insignificantly either. Here are the number from Firefox, running each function 10,000 time on the string "gohangasalamiimalasagnahog" (go hang a salami, i'm a lasagna hog).
- (Traverse from both ends): 797ms
- (Split/Reverse/Join): 969ms
- (Compare halves): 640ms
Of course, numbers varied slightly during each run, but overall the they were consistent relative to each other.
So there ya go. Nothing in particular I wanted to point out here; just found it interesting and thought you might too.
Think my code sucks? Got a better solution? Say so in the comments (you won't hurt my feelings).