文字列sを指定して、sで最も長い回文部分文字列を見つけます。sの最大長は1000と想定できます。
例1:
入力:「babad」
出力:「bab」
注:「aba」も有効な回答です。
例2:
入力: "cbbd"
出力: "bb"
出典:LeetCode
リンク:https ://leetcode-cn.com/problems/longest-palindromic-substring
著作権は控除ネットワークに属しています。商用転載の正式な許可書に連絡し、非商用転載の出典を明記してください。
アイデア1:[中央拡散法]以前に多くの回文のアナモフィックな質問が行われたことを覚えておいてくださいが、現在はほとんど同じようです。真実に戻ると、この質問では、各文字の間に#などのカスタム記号を挿入して、特定の最大パリンドローム文字列が奇数でなければならないようにすることができます。つまり、特定の文字の中央に配置する必要があります。 aa)2つは一緒に処理されます。その後、今私が考えることができるのは暴力だけです。それを中心に各キャラクターを繰り返し、別々に前後にトラバースして、最長のパリンドロームを見つけます。
複雑さは次のとおりです:o(n ^ 2)
class Solution:
def longestPalindrome(self, s: str) -> str:
s_2 = '#'
for i in range(len(s)):
s_2 += s[i] + '#'
max_s = 0
max_len = 0
for i in range(1, len(s_2)):
k = 1
while i - k >= 0 and i + k < len(s_2):
if s_2[i-k] == s_2[i+k]:
k += 1
else:
break
k -= 1
if max_len < k * 2 + 1:
max_s = i - k # 回文的起点
max_len = k * 2 + 1
# print(max_s, k, max_len)
# print(s_2)
# print(max_s, max_len)
# print(s_2[max_s:(max_s+max_len)].replace('#', ''))
return s_2[max_s:(max_s+max_len)].replace('#', '')
アイデア2:動的プログラミング
文字列strの場合、dp [i、j] = 1がstr [i ... j]が回文のサブストリングであることを意味し、dp [i + 1、j-1] = 1が存在する必要があるとします。このようにして、最長のパリンドロームのサブストリングを一連のサブ問題に分解することができます。これは、動的プログラミングを使用して解決できます。
最初に状態遷移方程式を作成します
上記の状態遷移方程式は、str [i] = str [j]の場合、str [i + 1 ... j-1]が回文ストリングである場合、str [i ... j]も回文ストリングであることを示しています。 ; str [i + 1 ... j-1]がパリンドローム文字列でない場合、str [i ... j]はパリンドローム文字列ではありません。
初期状態
-
dp [i] [i] = 1
-
dp [i] [i + 1] = 1 str [i] == str [i + 1]の場合
上記の式の意味は単一の文字であり、2つの同一の文字は回文列です。
再帰の場合、長さは2、3、4の順に計算されることに注意してください。
class Solution:
def longestPalindrome(self, s: str) -> str:
length = len(s)
dp = [[0]*length for _ in range(length)]
for i in range(length):
dp[i][i] = 1
max_i = 0
max_j = 0
max_len = 0
# 控制每次的区间长度-1
for k in range(1, length):
# i为区间起始坐标,j为区间结束坐标
for i in range(0, length - k):
j = i + k
if s[i] == s[j]:
# print(i, j)
if i == j - 1: # 长度为2,K=1时需要特判
dp[i][j] = 1
else:
dp[i][j] = dp[i+1][j-1]
if dp[i][j] == 1:
if k + 1 > max_len:
max_i = i
max_j = j
max_len = k + 1
else:
dp[i][j] = 0
return s[max_i:max_j+1]
アイデア3:Manacherのアルゴリズム
参照ドキュメント:https : //blog.csdn.net/weixin_43272781/article/details/89713050
参照ビデオ:UESTCACM週次アルゴリズム講義管理アルゴリズム
【アルゴリズム講座】【中国電子科学技術大学】【ACM】文字列アルゴリズム講座
要点をまとめます。
①定義されているいくつかの変数:
p []:p [i]は、iを中心とする回文列の半径を表します。
id:現在拡張されている最も遠い回文列の半径を表します(最大ではなく、一番右の回文列のみ)
mx:idを中心とした回文列の右境界の外側にある最初のポイントの位置を表します。
②繰り返し関係:
i、jがidを中心とする対称点であるとすると、p [j]がわかっている場合、p [i]は次のように計算する必要があります。
1. p [j]はidの回文文字列の内部にあり、次にp [i]も内部にあります。i+ jなので、p [i] = p [j] = p [2 * id-i]を計算できます。 = 2 * id(idは中心です)。
2. p [j]がidのパリンドローム文字列の範囲を超えると、p [i]の最小値もmx-iに等しくなります。
要約:p [i] = min(p [2 * id-i]、mx-i)
注:現在のp [i]を取得した後、最小半径がこの時点で取得されるため、iを中心とする回文列を拡張する必要があります。
複雑さ:O(n)
class Solution:
def longestPalindrome(self, s: str) -> str:
s_2 = '#'
for i in range(len(s)):
s_2 += s[i] + '#'
# print(s_2)
length = len(s_2)
# p[i]以i中心的回文半径
p = [1] * length
id = 0 # 某个延伸最远的回文字符串的中心(该回文串不一定最长)
mx = 1 # 某个延伸最远的回文字符串能达到的最右端的值+1(即在回文外的第一个值)
max_s = 0
max_r = 0
for i in range(length):
if i < mx:
p[i] = min(p[2*id-i], mx-i)
while i - p[i] >= 0 and i + p[i] < length and s_2[i - p[i]] == s_2[i + p[i]]: # 拓展i为中心的回文串
p[i] += 1
if i + p[i] > mx: # 如果以i为中心的回文串延伸的更远,则进行更新
mx = i + p[i]
id = i
if p[i] > max_r: # 跟新最大回文字符串
max_s = i
max_r = p[i]
# print(max_s, max_r)
return s_2[max_s-max_r+1:max_s+max_r-1].replace('#', '')