添加最少字符使字符串整体都是回文字符串

添加最少字符使字符串整体都是回文字符串

添加最少字符拼凑回文字符串

【题目】
给定一个字符串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"为例

  1. 字符串s的长度n为6,最长回文子序列字符串slps的长度m为3,回文字符串res(字符数组形式)的长度为2n-m=9;
  2. 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’];
  3. 添加左匹配字符后:[‘A’, ‘C’, ‘1’, 0, 0, 0, 0, ‘C’, ‘A’],添加右匹配字符后:[‘A’, ‘C’, ‘1’, 0, 0, 0, ‘1’, ‘C’, ‘A’];
  4. s_l = 2, s_l_cur=3, 添加左匹配边界后[‘A’, ‘C’, ‘1’, ‘B’, 0, ‘B’, ‘1’, ‘C’, ‘A’],s_r = 3, s_r_cur = 3, 无右匹配边界;
  5. 添加左匹配字符:[‘A’, ‘C’, ‘1’, ‘B’,‘2’, ‘B’, ‘1’, ‘C’, ‘A’];
  6. s_l = 4, s_l_cur=3, 字符完全匹配,循环结束。


    整个过程遍历比较字符串sslps,申请开辟额外空间用于存储res,因此算法时间复杂度为 O ( N + M ) O(N+M) O(N+M),空间复杂度为 O ( N − M ) O(N-M) O(NM)
相应代码
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"

有任何疑问和建议,欢迎在评论区留言和指正!

感谢您所花费的时间与精力!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 答1: 要使一个字符串变成串,需要添加最少字符数取决于字符串本身。如果字符串本身已经是串,则不需要添加任何字符。否则,需要添加字符使其变成串。 可以使用动态规划来解决这个问题。具体来说,假设字符串为s,长度为n,定义dp[i][j]表示子串s[i...j]需要添加最少字符数,使得该子串变成串。则有以下状态转移方程: 当i = j时,dp[i][j] = 0,因为单个字符本身就是串。 当s[i] = s[j]时,dp[i][j] = dp[i+1][j-1],因为如果s[i]和s[j]相等,那么只需要让子串s[i+1...j-1]变成串即可。 否则,dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1,因为如果s[i]和s[j]不相等,那么必须要在s[i]和s[j]中间添加一个字符,使得子串s[i...j]变成串,那么这个字符可以是s[i],也可以是s[j],所以需要取添加字符后变成串所需的最小次数。 最终,dp[0][n-1]就是整个字符串s需要添加最少字符数,使得它变成串。 ### 答2: 要使字符串str变串,需要在字符串的前后添加若干个字符添加字符的数量最少是str的长度减去str的子串的长度。 首先,我们需要找出str的最长子串的长度。可以通过遍历字符串的所有子串,判断是否为串,从而得到最长子串的长度。 接下来,将str的长度减去最长子串的长度,得到需要添加字符的数量。因为添加字符需要同时出现在str的前后,所以数量是原字符串长度和最长子串长度的差值。 举个例子,假设str为"abcbade",该字符串的最长子串为"abcba",长度为5。字符串str的长度为7,所以需要添加字符数量为7-5=2。 所以需要添加至少2个字符才能使字符串str变串。 ### 答3: 已知字符串str,如果它本身就是一个串,那么不需要添加任何字符;如果str不是一个串,我们需要添加至少的字符使它变成一个串。 为了使str变成一个串,我们可以在字符串的某个位置添加字符。考虑一个最简单的情况,我们将添加字符在str的最前面,即在str的首字符添加一个与str首字符相同的字符。这样,str的首字符和最前添加字符都是相同的,这种情况下,我们只需要添加一个字符。 但是,如果str的首字符和尾字符不相同,我们必须至少添加两个字符。我们可以在str的首字符添加一个与str首字符相同的字符,再在str的尾字符添加一个与str尾字符相同的字符。这样,在添加的两个字符的帮助下,str首字符和最前添加字符相同,str尾字符和最后添加字符相同,形成了一个串。 所以,总结起来,我们需要至少添加字符数就等于str的首字符和尾字符不相同的情况下需要添加字符数。即如果str的首字符和尾字符相同,我们只需要添加一个字符;如果不相同,我们需要添加两个字符。 综上所述,已知字符串str,至少需要添加字符数为1或2,具体取决于str的首字符和尾字符是否相同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值