BOBOBK

Longest Palindromic Substring Algorithm - Manacher

TECHNOLOGY

While solving LeetCode problems, I encountered a question about finding the longest palindromic substring.

The official solutions provide four methods:

  1. Brute Force
  2. Dynamic Programming
  3. Center Expansion Algorithm
  4. The Manacher method, which we will introduce today.

Before introducing the algorithm, let’s first define what a palindrome is. Simply put, a palindrome is a string that reads the same forwards and backwards, such as “aba”, “上海自来水来自海上” (Shanghai tap water comes from the sea), etc. The longest palindromic substring of a given string is the longest substring of that string that is also a palindrome.

Manacher’s algorithm is the most efficient, with a time complexity of O(n).


Manacher Algorithm Principle and Implementation

Manacher’s algorithm provides a clever way to handle palindromes of both odd and even lengths. The specific approach is to insert a delimiter ‘#’ between every two characters in the original string, and also insert ‘(@)’ at the beginning and a delimiter (’$’) at the end.


Calculating the Len Array

Calculate Len[i] from left to right. When calculating Len[i], Len[j] (where 0 <= j < i) has already been calculated. Let P be the maximum right endpoint of the longest palindromic substring calculated so far, and let po be the position where this maximum value was achieved. There are two cases:

Case 1: i <= P

Find the symmetric position of i with respect to po, let’s call it j. If Len[j] < P - i:

This means that the palindrome centered at j is entirely within the palindrome centered at po. Since j and i are symmetric with respect to po, and by the definition of a palindrome, a palindrome read backwards is still a palindrome, the length of the palindrome centered at i is at least the same as the palindrome centered at j, i.e., Len[i] >= Len[j]. Because Len[j] < P - i, it implies i + Len[j] < P. By symmetry, we know Len[i] = Len[j].

If Len[j] >= P - i, by symmetry, this means the palindrome centered at i might extend beyond P. The portion beyond P has not yet been matched, so we must start matching one by one from position P+1 until a mismatch occurs. This process will then update P, the corresponding po, and Len[i].

Case 2: i > P

If i is greater than P, it means that no part of the palindrome centered at i has been matched yet. In this situation, we can only proceed by matching characters one by one. After the matching is complete, P, the corresponding po, and Len[i] must be updated.


Python3 Implementation of Manacher Algorithm

    def longestPalindrome(self, s: str) -> str:
        s = "@#"+"#".join(s)+"#$"
        mx,ans,p0=0,0,0     ##### Rightmost position, maximum length, center position
        Len = [0]*len(s)
        mxstr = ''
        for i in range(len(s)):
            if mx>i:
                Len[i]=min(mx-i,Len[2*p0 - i])
            else:
                Len[i] = 1
            while i+Len[i]<len(s) and  s[i-Len[i]]==s[i+Len[i]]:
                Len[i] += 1
            if Len[i]+i > mx :
                mx=Len[i]+i
                p0 = i
            if Len[i] >= ans:
                mxstr = s[i-Len[i]+1:i+Len[i]] 
                ans=Len[i]

        return  mxstr.replace("#","").replace("$","")

Related