添加最少字符使字符串整体都是回文字符串
添加最少字符拼凑回文字符串
【题目】
给定一个字符串s,如果可以在s的任意位置添加字符,请返回在添加字符最少的情况下,让s整体都是回文字符串的一种结果。
【举例】
s=“ABA”。s本身就是回文串,不需要添加字符,所以返回"ABA"。
s=“AB”。可以在’A’之前添加’B’,使s整体都是回文串,故可以返回"BAB"。
也可以在’B’之后添加’A’,使s整体都是回文串,故也可以返回"ABA"。
总之,只要添加的字符数最少,只返回其中一种结果即可。
算法思路
动态规划,dp[i][j]
为i到j任意添加字符构成回文的最少数量
dp[i][i] = 0
;
dp[i][i + 1] = 0 if dp[i] == dp[i + 1] 1
;
dp[i][j] = dp[i + 1][j - 1] if dp[i] == dp[j] min(dp[i][j - 1], dp[i + 1][j]) + 1
;
n为字符串s的长度,m = dp[0][n - 1]
为整个字符串任意添加字符构成回文的最少数量;
时间复杂度为
O
(
N
2
)
O(N^2)
O(N2),空间复杂度为
O
(
N
2
)
O(N^2)
O(N2)。
相应代码
def gen_pal_str1(s):
if s is None or len(s) <= 1:
return s
dp = get_dp(s)
res = [0 for i in range(len(s) + dp[0][len(s) - 1])]
s_l = 0
s_r = len(s) - 1
res_l = 0
res_r = len(res) - 1
while s_l <= s_r:
if s[s_l] == s[s_r]:
res[res_l] = s[s_l]
res[res_r] = s[s_r]
s_l += 1
res_l += 1
s_r -= 1
res_r -= 1
else:
# 两边添加s[s_l],中间剩s_l + 1 ~ s_r处理
if dp[s_l][s_r - 1] >= dp[s_l + 1][s_r]:
res[res_l] = s[s_l]
res[res_r] = s[s_l]
s_l += 1
res_l += 1
res_r -= 1
# 两边添加s[s_r],中间剩s_l ~ s_r - 1处理
else:
res[res_l] = s[s_r]
res[res_r] = s[s_r]
s_r -= 1
res_l += 1
res_r -= 1
return ''.join(res)
# 动态规划
# dp[i][j]为i到j任意添加字符构成回文的最少数量
# 两端相等, dp[i][j] = dp[i + 1][j - 1]
# 两端不相等, dp[i][j] = min(dp[i][j - 1], dp[i + 1][j]) + 1
def get_dp(s):
n = len(s)
dp = [[0 for i in range(n)] for j in range(n)]
for j in range(1, n):
if s[j - 1] != s[j]:
dp[j - 1][j] = 1
i = j - 2
while i >= 0:
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1]
else:
if dp[i + 1][j] <= dp[i][j - 1]:
dp[i][j] = dp[i + 1][j] + 1
else:
dp[i][j] = dp[i][j - 1] + 1
i -= 1
return dp
# 简单测试
if __name__ == '__main__':
s = "ABA"
print(gen_pal_str1(s)) # "ABA"
s = "AB"
print(gen_pal_str1(s)) # "ABA"
s = "ABCDE"
print(gen_pal_str1(s)) # "ABCDEDCBA"
基于最长回文子序列字符串添加最少字符拼凑回文字符串
【进阶题目】
给定一个字符串s,再给定s的最长回文子序列字符串slps,
请返回在添加字符最少的情况下,让s整体都是回文字符串的一种结果。
进阶问题比原问题多了一个参数,请做到时间复杂度比原问题的实现低。
【举例】
s=“A1B21C”,slps=“121”,返回"AC1B2B1CA"或者"CA1B2B1AC"。
总之,只要是添加的字符数最少,只返回其中一种结果即可。
算法思路
基于最长回文子序列字符串slps,分别找到对应s中字符的位置,以此为界限;
依次将左匹配边界和右匹配边界都添加到res里两侧,左匹配边界:[s_l, s_l_cur),右匹配边界(s_r_cur, s_r];
在左里侧添加左匹配字符,在右里侧添加左匹配字符;
循环直至slps所有字符完全匹配。
以 s=“A1B21C”,slps="121"为例
- 字符串s的长度n为6,最长回文子序列字符串slps的长度m为3,回文字符串res(字符数组形式)的长度为2n-m=9;
- s_l = 0, s_l_cur=1, 添加左匹配边界后res=[‘A’, 0, 0, 0, 0, 0, 0, 0, ‘A’],s_r= 5, s_r_cur=4, 添加右匹配边界后res=[‘A’, ‘C’, 0, 0, 0, 0, 0, ‘C’, ‘A’];
- 添加左匹配字符后:[‘A’, ‘C’, ‘1’, 0, 0, 0, 0, ‘C’, ‘A’],添加右匹配字符后:[‘A’, ‘C’, ‘1’, 0, 0, 0, ‘1’, ‘C’, ‘A’];
- s_l = 2, s_l_cur=3, 添加左匹配边界后[‘A’, ‘C’, ‘1’, ‘B’, 0, ‘B’, ‘1’, ‘C’, ‘A’],s_r = 3, s_r_cur = 3, 无右匹配边界;
- 添加左匹配字符:[‘A’, ‘C’, ‘1’, ‘B’,‘2’, ‘B’, ‘1’, ‘C’, ‘A’];
- s_l = 4, s_l_cur=3, 字符完全匹配,循环结束。
整个过程遍历比较字符串s
和slps
,申请开辟额外空间用于存储res
,因此算法时间复杂度为 O ( N + M ) O(N+M) O(N+M),空间复杂度为 O ( N − M ) O(N-M) O(N−M)。
相应代码
def gen_pal_str2(s, slps):
n = len(s)
m = len(slps)
s_l = 0
s_r = n - 1
s_l_cur = s_l
s_r_cur = s_r
slps_l = 0
slps_r = m - 1
res = [0 for i in range(2 * n - m)]
res_l = 0
res_r = len(res) - 1
while slps_l <= slps_r:
while s[s_l_cur] != slps[slps_l]:
s_l_cur += 1
while s[s_r_cur] != slps[slps_r]:
s_r_cur -= 1
res_l, res_r = gen_res(res, s, s_l, s_l_cur, s_r_cur, s_r, res_l, res_r)
res[res_l] = s[s_l_cur]
res[res_r] = s[s_r_cur]
s_l_cur += 1
s_r_cur -= 1
s_l = s_l_cur
s_r = s_r_cur
res_l += 1
res_r -= 1
slps_l += 1
slps_r -= 1
print(res)
return ''.join(res)
# 左半区[s_l, s_l_cur), 右半区(s_r, s_r_cur]添加至结果
def gen_res(res, s, s_l, s_l_cur, s_r_cur, s_r, res_l, res_r):
for i in range(s_l, s_l_cur):
res[res_l] = s[i]
res_l += 1
res[res_r] = s[i]
res_r -= 1
for i in range(s_r, s_r_cur, -1):
res[res_l] = s[i]
res_l += 1
res[res_r] = s[i]
res_r -= 1
return res_l, res_r
# 简单测试
if __name__ == '__main__':
s = "A1B21C"
slps = "121"
print(gen_pal_str2(s, slps)) # "AC1B2B1CA"
有任何疑问和建议,欢迎在评论区留言和指正!
感谢您所花费的时间与精力!