Manacher's Algorithm horse-drawn cart algorithm (python implementation)

Hand-printing + drawing, no reprinting

The horse-drawn cart algorithm Manacher's Algorithm is a linear method used to find the longest palindrome substring of a string. The biggest contribution of this method is to increase the time complexity to linear O(n).

—The horse-drawn cart algorithm uses the characteristics of palindrome to avoid repeated calculations —

Step 1: Insert a "#" between each character of the input string, such as: S="abcbaade", after inserting the string becomes S'="#a#b#c#b#a#a #d#e#". It is easy to know that the number of inserted "#" and the number of characters in the string have different parity, so the length of the processed string becomes an odd number. This step unifies the parity of the string

Step 2: Create a new array P[]. The length of P[] is the same as the length of the processed S string. P[i] stores the palindrome radius at S'[ i ], which means S'[ i] The length of the largest palindrome centered at the center. For example, the longest palindrome substring centered at S'[ i] is S'[ i, r ], then P[ i] = r-i + 1. In this way, finally traverse the array P[] and take the maximum value. If P[ i ]=1, it means that the palindrome is T[ i] itself.

! The special nature of the P[] array: The value of P[ i] corresponding to S'[ i] minus 1 is the length of the longest palindrome corresponding to the character in the original string S

How to find the array P[]

Calculate the array P[] from left to right, M is the center position of the largest palindrome obtained previously, and R and L are the values ​​that the palindrome can reach the right end and the left end, respectively

Case 1: When i <= R, use the characteristics of the palindrome to find the symmetrical point j of point i with respect to M, and its value is j = M*2-i, because points j and i are the largest centered on M Within the range of the palindrome [L, R]

If P[ j] <R-i (ie the distance between j and L), it means that the palindrome centered at point j does not exceed the range [L, R]. P[ j] can be seen from the characteristics of the palindrome = P[ i ].

If P[ j] >= R-i (that is, the left end of the largest palindrome with j as the center exceeds L), then the characters between the palindrome characteristics [L, j] do not need to be verified and need to be verified Is the excess part> whether there is a symmetrical character about i in the R part, continue to match one by one from R+1 until it can't match.

Case 2: When i> R, the characteristics of palindrome cannot be used, starting from i + 1 to match one by one

Example: LeetCode maximum palindrome substring

  1. Palindromic longest substring - stay button (LeetCode) LeetCode palindromic longest substring Given a string s, s to find the longest substring palindromic. You can assume that the maximum length of s is 1000.


Example 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

Example 2:

输入: "cbbd"
输出: "bb"

Python3 sample code:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        '''最长公共子串(动态规划) --- 超时'''
        # if len(s) == 1:
        #     return s
        # if len(s) == 0:
        #     return ""
        # l = len(s)
        # maxLength = 0
        # left = 0
        # right = 0
        # array = [[0 for i in range(0,l + 2)] for j in range(0,l + 2)]
        # for i in range(0,l):
        #     for j in range(0,l):
        #         if s[i] == s[j]:
        #             now = array[i][j+1] + 1
        #             array[i+1][j] = now
        #             if now > maxLength:
        #                 status = 0
        #                 for t in range(0,int(now/2) + 1):
        #                     if s[j + t] != s[j + now - t - 1]:
        #                         status = 1
        #                 if status == 0:
        #                     maxLength = now
        #                     left = j
        #                     right = j + maxLength 
        # return s[left:right]
        
        '''马拉车算法 --- 时间复杂度降低到线性'''
        S = "^#"
        for i in range(0,len(s)):
            S = S + s[i] + "#"
        S = S + "$"
        #print(S)
        P = [0 for i in range(0,len(S))]
        M = 0
        R = 0
        for i in range(1,len(S) - 1):
            j = M * 2 - i
            if R > i:
                if R - i > P[j]:
                    P[i] = P[j]
                else:
                    P[i] = R - i
            else:
                P[i] = 0
            # print("P[i]:" + str(P[i]))
            # print("i:" + str(i))
            # print("S[i]" + str(S[i]))
            while S[i + P[i] + 1] == S[i - P[i] - 1]:
                P[i] = P[i] + 1
                # print(P[i])
            
            if i + P[i] > R:
                R = i + P[i]
                M = i
        #print(P)
        Center = 0
        maxl = 0
        for i in range(1,len(S) - 1):
            if P[i] >= maxl:
                maxl = P[i]
                Center = i
        
        start = int((Center - maxl)/ 2)
        end = start + maxl
        # print(start)
        # print(end)
        return s[start:end]

Guess you like

Origin blog.csdn.net/weixin_42363722/article/details/110871649