Manacherのアルゴリズム馬車アルゴリズム(Python実装)

手刷り+図面、転載なし

馬車アルゴリズムManacher'sAlgorithmは、文字列の最長の回文部分文字列を見つけるために使用される線形手法です。この手法の最大の貢献は、時間計算量を線形O(n)に増やすことです。

—馬車アルゴリズムは、回文の特性を使用して、計算の繰り返しを回避します—

手順1:入力文字列の各文字の間に「#」を挿入します(例:S = "abcbaade")。文字列を挿入すると、S '= "#a#b#c#b#a#a#d#e#になります。 "。挿入された「#」の数と文字列の文字数のパリティが異なることは容易に理解でき、処理される文字列の長さが奇数になり、文字列のパリティが統一されます

手順2:新しい配列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]から1を引いたものに対応するP [i]の値は、元の文字列Sの文字に対応する最長の回文の長さです。

配列P []を見つける方法

配列P []を左から右に計算します。Mは以前に取得した最大のパリンドロームの中心位置であり、RとLはパリンドロームがそれぞれ右端と左端に到達できる値です。

ケース1:i <= Rの場合、回文の特性を使用して、Mに関して点iの対称点jを見つけます。点jとiは最大の中心であるため、その値はj = M * 2-iです。 M上回文の範囲内[L、R]

P [j] <R-i(つまり、jとLの間の距離)の場合、点jを中心とする回文が範囲[L、R]を超えないことを意味します。P[j]は、回文の特性からわかります。 = P [i]。

P [j]> = Ri(つまり、jを中心とする最大のパリンドロームの左端がLを超える)の場合、パリンドローム特性[L、j]の間の文字を検証する必要はなく、次のようにする必要があります。検証済み余分な部分は> R部分にiについて対称文字があるかどうか、一致しなくなるまでR +1から1つずつ一致し続けます。

ケース2:i> Rの場合、i + 1から開始して、1つずつ一致するパリンドロームの特性を使用できません。

例:LeetCodeの最大回文部分文字列

  1. 回文最長部分文字列-滞在ボタン(LeetCode)LeetCode回文最長部分文字列文字列s、sを指定して、最長の部分文字列palindromicを検索します。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