给定一个字符串 s s s,找到 s s s中最长的回文子串。例:‘bababd’的最长的回文子串为’babab’。记字符串 s s s长度为 n n n。
暴力算法
def is_palinedrome(string):
for s in range(len(string) // 2):
if string[s] != string[len(string) - s - 1]:
return False
else:
return True
def longest_palindrome(string):
max_length = 0
target_string, current_string = '', ''
for index, s1 in enumerate(string):
current_string = ''
for s2 in string[index:]:
current_string += s2
if is_palinedrome(current_string):
max_length = max(max_length, current_length)
target_string = current_string if max_length == len(current_string) else target_string
return target_string
寻找需要两层循环,判断需要一层循环,故时间复杂度为 O ( n 3 ) O(n^3) O(n3);变量个数为常数个,故空间复杂度为 O ( 1 ) O(1) O(1)。
暴力算法优化:动态规划
状态:
p
[
j
j
]
[
i
i
]
p[jj][ii]
p[jj][ii] 表示
s
[
j
j
:
i
i
+
1
]
s[jj:ii+1]
s[jj:ii+1]是否为回文子串。
状态转移方程:
p
[
j
j
]
[
i
i
]
=
(
s
[
j
j
]
=
=
s
[
i
i
]
a
n
d
p
[
j
j
+
1
]
[
i
i
−
1
]
)
p[jj][ii]=(s[jj]==s[ii]\ \ and\ \ p[jj+1][ii-1])
p[jj][ii]=(s[jj]==s[ii] and p[jj+1][ii−1])。即:子串为回文子串的条件为最外层两侧字符相同且内层为回文子串。
边界条件:
j
j
−
i
i
≤
2
jj-ii\le 2
jj−ii≤2。即:当最内层子串长度为2或3且两侧字符相同时为回文子串。
def longest_palindrome(string):
size = len(string)
if size <= 1:
return string
max_length = 0
begin, end = 0, 0
p = [[False for _ in range(size)] for _ in range(size)]
for index in range(size):
p[index][index] = True
for ii in range(size):
for jj in range(ii):
if string[ii] == string[jj]:
if ii - jj <= 2:
p[jj][ii] = True
else:
p[jj][ii] = p[jj + 1][ii - 1]
else:
p[jj][ii] = False
if p[jj][ii] is True:
if max_length < ii - jj + 1:
max_length = ii - jj + 1
begin, end = jj, ii
# break
# max_length = max(ii - jj + 1, max_length)
# begin, end = jj, ii
return string[begin:end + 1]
循环的层数为两层,故时间复杂度为
O
(
n
2
)
O(n^2)
O(n2);由于需要用数组存储动态规划结果,故空间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。
为什么不能在注释处跳出循环:当长回文子串包含短回文子串时,若跳出循环可能会导致短回文子串没有被遍历到,即:动态规划结果保存不全。进一步导致遍历长回文子串时内层为False
,使长回文子串判定错误。例:‘abababa’。
为什么只能在max_length
改变时改变起点与终点:当长回文子串包含短回文子串时,改变起点与终点会使输出不全。
中心扩展
回文串一定是对称的,所以可以每次循环选择一个中心,进行左右扩展,判断左右字符是否相等即可。需要注意的是,当回文串长度为奇数时,中心为一个字符;否则为两个字符。故需要进行两次循环。
def longest_palindrome(string):
size = len(string)
if size <= 1:
return string
max_length, target_string = 0, ''
for ii in range(1, size - 1):
current_string = string[ii]
for jj in range(1, size):
if ii < jj or ii + jj == size:
break
elif string[ii - jj] == string[ii + jj]:
current_string = string[ii - jj] + current_string + string[ii + jj]
else:
break
max_length = max(max_length, current_length)
target_string = current_string if max_length == len(current_string) else target_string
for ii in range(size - 1):
if string[ii] != string[ii + 1]:
continue
current_string = string[ii] * 2
for jj in range(1, size):
if ii < jj or ii + jj + 1 == size:
break
elif string[ii - jj] == string[ii + jj + 1]:
current_string = string[ii - jj] + current_string + string[ii + jj + 1]
else:
break
max_length = max(max_length, current_length)
target_string = current_string if max_length == len(current_string) else target_string
return target_string
两次循环的层数均为两层,故时间复杂度为 O ( n 2 ) O(n^2) O(n2);变量个数为常数个,故空间复杂度为 O ( 1 ) O(1) O(1)。