Manacher‘s Algorithm 马拉车算法(python实现)

纯手打+做图,禁止转载

马拉车算法 Manacher‘s Algorithm 是用来查找一个字符串的最长回文子串的线性方法,这个方法的最大贡献是在于将时间复杂度提升到了线性O(n)。

—马拉车算法利用回文串的特性来避免重复计算—

第一步:在输入字符串每个字符之间都插入一个“#”,如:S=“abcbaade”,插入后字符串变为S’=“#a#b#c#b#a#a#d#e#”。易知,插入的“#”的个数和字符串字符个数奇偶性不同,所以处理后的字符串长度变为奇数,这一步统一了字符串的奇偶性

第二步:新建数组P[],P[]的长度和处理后的S字符串长度一样,P[i]中存放的是S’[ i ]处的回文半径,表示S’[ i ]为中心的最大回文串的长度,如以S’[ i ]为中心的最长回文子串的为S’[ i , r ],那么P[ i ]= r - i + 1。这样最后遍历数组P[ ],取其中最大值即可。若P[ i ]=1表示该回文串就是T[ i ]本身。

!P[]数组的特殊性质:S’[ i ]对应的P[ i ]的值减去1就是该字符在原字符串S中对应最长回文串的长度

如何求数组P[]

从左往右计算数组P[],M为之前取得最大回文串的中心位置,而R和L分别为该回文串能到达最右端和最左端的值

情况1:当 i <= R时,利用回文串的特性,找到点 i 关于 M 的对称点 j ,其值为 j = M*2 - i,因为点 j 、i 在以M为中心的最大回文串的范围内 [L , R]

如果P[ j ] < R - i (即 j 和 L 之间的距离),说明以点j为中心的回文串没有超出范围 [L , R],由回文串的特性可知P[ j ] = P[ i ]。

如果P[ j ] >= R - i (即 j 为中心的最大回文串左端超过了L),那么由回文串特性 [L , j] 之间的字符是不需要验证的,需要验证的是超出部分 > R部分是否有关于 i 对称字符,继续从R+1开始一一匹配,直到不能匹配为止。

情况2:当 i > R时,无法利用到回文串的特性,从 i + 1开始一一匹配

例子:LeetCode 最大回文子串

  1. 最长回文子串 - 力扣(LeetCode)

    LeetCode 最长回文子串
    给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

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

示例 2:

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

Python3示例代码:

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]

猜你喜欢

转载自blog.csdn.net/weixin_42363722/article/details/110871649
今日推荐