Skip to content

Instantly share code, notes, and snippets.

@keif
Created April 22, 2015 05:09
Show Gist options
  • Save keif/763b6d725e23bb1b91f5 to your computer and use it in GitHub Desktop.
Save keif/763b6d725e23bb1b91f5 to your computer and use it in GitHub Desktop.
A VERY INCOMPLETE AND UNTESTED JavaScript code rip of Manacher's algorithm. I want to get this into node to actually play with it.
/**
* Computes the longest palindromic substring in linear time
* using Manacher's algorithm.
*
* Credits: The code is lifted from the following excellent reference
* http://www.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html
**/
var Manacher = (function () {
var palindromic = [], // p[i] = length of longest palindromic substring of t, centered at i
string, // original string
transform, // transformed string
manacher,
preprocess,
longestPalindromicSubstring,
main;
manacher = function (string) {
this.string = string;
transform = preprocess(string);
palindromic = [transform.length];
var center = 0,
right = 0;
for (var i = 1; i < transform.length - 1; i++) {
var mirror = 2 * center - i;
if (right > i) {
palindromic[i] = Math.min(right - i, palindromic[mirror]);
}
// attempt to expand palindrome centered at i
while (transform[i + (1 + palindromic[i])] === transform[i - (1 + palindromic[i])]) {
palindromic[i]++;
}
// if palindrome centered at i expands past right,
// adjust center based on expanded palindrome.
if (i + palindromic[i] > right) {
center = i;
right = i + palindromic[i];
}
}
};
// Transform s into t.
// For example, if s = "abba", then t = "$#a#b#b#a#@"
// the # are interleaved to avoid even/odd-length palindromes uniformly
// $ and @ are prepended and appended to each end to avoid bounds checking
preprocess = function (s) {
var t = [s.length * 2 + 3];
t[0] = '$';
t[s.length * 2 + 2] = '@';
for (var i = 0; i < s.length; i++) {
t[2 * i + 1] = '#';
t[2 * i + 2] = s.charAt(i);
}
t[s.length * 2 + 1] = '#';
return t;
};
// longest palindromic substring
longestPalindromicSubstring = function (i) {
if (!i) {
var length = 0, // length of longest palindromic substring
center = 0; // center of longest palindromic substring
for (var i = 1; i < palindromic.length - 1; i++) {
if (palindromic[i] > length) {
length = palindromic[i];
center = i;
}
}
return string.substring((center - 1 - length) / 2, (center - 1 + length) / 2);
} else {
i = i + 2;
var length = palindromic[i],
center = i;
return string.substring((center - 1 - length) / 2, (center - 1 + length) / 2);
}
}
// test client
main = function(args) {
var s = args[0],
manacher = this.manacher(s);
console.log(this.longestPalindromicSubstring());
for (var i = 0; i < 2 * s.length; i++) {
console.log(i + ": " + this.longestPalindromicSubstring(i));
}
};
return {
manacher: manacher,
preprocess: preprocess,
longestPalindromicSubstring: longestPalindromicSubstring,
init: main
}
})(Manacher);
var palindrome = "tacocat";
Manacher.init(palindrome);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment