Longest palindromic substring

In computer science, the longest palindromic substring or longest symmetric factor problem is the problem of finding a maximum-length contiguous substring of a given string that is also a palindrome. For example, the longest palindromic substring of "bananas" is "anana". The longest palindromic substring is not guaranteed to be unique; for example, in the string "abracadabra", there is no palindromic substring with length greater than three, but there are two palindromic substrings with length three, namely, "aca" and "ada". In some applications it may be necessary to return all maximal palindromic substrings (that is, all substrings that are themselves palindromes and cannot be extended to larger palindromic substrings) rather than returning only one substring or returning the maximum length of a palindromic substring.

Contents

To find in linear time a longest palindrome in a string, an algorithm may take advantage of the following characteristics or observations about a palindrome and a sub-palindrome:

The left side of a palindrome is a mirror image of its right side.

(Case 1) A third palindrome whose center is within the right side of a first palindrome will have exactly the same length as a second palindrome anchored at the mirror center on the left side, if the second palindrome is within the bounds of the first palindrome by at least one character (not meeting the left bound of the first palindrome). Such as "dacabacad", the whole string is the first palindrome, "aca" in the left side as second palindrome, "aca" in the right side as third palindrome. In this case, the second and third palindrome have exactly the same length.

(Case 2) If the second palindrome meets or extends beyond the left bound of the first palindrome, then the distance from the center of the second palindrome to the left bound of the first palindrome is exactly equal to the distance from the center of the third palindrome to the right bound of the first palindrome.

To find the length of the third palindrome under Case 2, the next character after the right outermost character of the first palindrome would then be compared with its mirror character about the center of the third palindrome, until there is no match or no more characters to compare.

(Case 3) Neither the first nor second palindrome provides information to help determine the palindromic length of a fourth palindrome whose center is outside the right side of the first palindrome.

It is therefore desirable to have a palindrome as a reference (i.e., the role of the first palindrome) that possesses characters farthest to the right in a string when determining from left to right the palindromic length of a substring in the string (and consequently, the third palindrome in Case 2 and the fourth palindrome in Case 3 could replace the first palindrome to become the new reference).

Regarding the time complexity of palindromic length determination for each character in a string: there is no character comparison for Case 1, while for Cases 2 and 3 only the characters in the string beyond the right outermost character of the reference palindrome are candidates for comparison (and consequently Case 3 always results in a new reference palindrome while Case 2 does so only if the third palindrome is actually longer than its guaranteed minimum length).

For even-length palindromes, the center is at the boundary of the two characters in the middle.

importjava.util.Arrays;publicclassManachersAlgorithm{publicstaticStringfindLongestPalindrome(Strings){if(s==null||s.length()==0)return"";char[]s2=addBoundaries(s.toCharArray());int[]p=newint[s2.length];intc=0,r=0;// Here the first element in s2 has been processed.intm=0,n=0;// The walking indices to compare if two elements are the same.for(inti=1;i<s2.length;i++){if(i>r){p[i]=0;m=i-1;n=i+1;}else{inti2=c*2-i;if(p[i2]<(r-i-1)){p[i]=p[i2];m=-1;// This signals bypassing the while loop below. }else{p[i]=r-i;n=r+1;m=i*2-n;}}while(m>=0&&n<s2.length&&s2[m]==s2[n]){p[i]++;m--;n++;}if((i+p[i])>r){c=i;r=i+p[i];}}intlen=0;c=0;for(inti=1;i<s2.length;i++){if(len<p[i]){len=p[i];c=i;}}char[]ss=Arrays.copyOfRange(s2,c-len,c+len+1);returnString.valueOf(removeBoundaries(ss));}privatestaticchar[]addBoundaries(char[]cs){if(cs==null||cs.length==0)return"||".toCharArray();char[]cs2=newchar[cs.length*2+1];for(inti=0;i<(cs2.length-1);i=i+2){cs2[i]='|';cs2[i+1]=cs[i/2];}cs2[cs2.length-1]='|';returncs2;}privatestaticchar[]removeBoundaries(char[]cs){if(cs==null||cs.length<3)return"".toCharArray();char[]cs2=newchar[(cs.length-1)/2];for(inti=0;i<cs2.length;i++){cs2[i]=cs[i*2+1];}returncs2;}}