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

数据结构与算法 专栏收录该内容
168 篇文章 4 订阅

【题目】

  给定一个字符串str,如果可以在str的任意位置添加字符,请返回在添加字符最少的情况下,让str整体都是回文字符串的结果。

【进阶题目】

  给定一个字符串str,再给定str的最长回文子序列字符串strlps,请返回在添加字符最少的情况下,让str整体都是回文字符串的一种结果。进阶问题比原问题多了一个参数,请做到时间复杂度比原问题的实现低。

【基本思路】

  原问题。首先考虑,如果可以在str的任意位置添加字符,最少需要添加几个字符就可以让str整体都是回文字符串。这个问题可以使用动态规划解决。如果str的长度为N,生成N×N的dp矩阵,dp[i][j]的含义是子串str[i…j]最少添加几个字符可以使str[i…j]整体都是回文串。dp[i][j]的求法如下:

  • 如果i == j,说明此时只有一个字符,本身就是回文串,dp[i][j] = 0。

  • 如果str[i…j]有两个字符,如果这个字符相同dp[i][j] = 0。否则dp[i][j] = 1。

  • 如果str[i…j]多于两个字母,如果str[i] == str[j]。则dp[i][j] = dp[i+1][j-1]。否则,dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1。为什么呢?举例说明,假设str = “ABC”,可以先将“BC”变成回文串“BCB”,然后在末尾加“A”,也可以先将“AB”变成回文串“ABA”,然后在最前面加“C”。即可以先处理str[i+1…j]然后末尾加str[i],也可以先处理str[[i…j-1]然后开头加str[j]。

接下来介绍如何根据dp矩阵,求在添加字符最少的情况下,让str整体都是回文字符串的一种结果。首先,dp[0][N-1]的值代表整个字符串最少需要添加几个字符,所以,如果最后的结果记为字符串res,res的长度为 N + dp[0][N-1],然后依次设置res左右两头的字符。具体过程如下:

  • 如果str[i] == str[j],那么str[i…j]变成回文串的最终结果为 str[i] + str[i+1][j-1]变成回文串的结果 + str[j],此时res的左右两头字符为str[i],然后根据str[i+1][j-1]和矩阵dp确定res的中间部分。

  • 如果str[i] != str[j],判断dp[i][j-1]和dp[i+1][j]哪个小,如果dp[i][j-1]小,那么str变成回文串的结果为 str[j] + str[i][j-1]变成回文串的结果 + str[j],此时res左右两头的字符为str[j],然后根据str[i][j-1]和矩阵dp确定res的中间部分。如果dp[i+1][j]小,过程同上。

下面是使用python3.5实现的代码

#添加最少字符使字符串整体都是回文字符串
#原问题
def getPalindrome(str1):
    def getdp(str1):
        dp = [[0 for i in range(len(str1))] for j in range(len(str1))]
        for j in range(1, len(str1)):
            dp[j-1][j] = 0 if str1[j-1] == str1[j] else 1
            for i in range(j-2, -1, -1):
                if str1[i] == str1[j]:
                    dp[i][j] = dp[i+1][j-1]
                else:
                    dp[i][j] = min(dp[i+1][j], dp[i][j-1]) + 1
        return dp


    if str1 == None or len(str1) < 2:
        return str1
    dp = getdp(str1)
    res = [0 for i in range(len(str1)+dp[0][len(str1)-1])]
    i = 0
    j = len(str1) - 1
    resl = 0
    resr = len(res) - 1
    while i <= j:
        if str1[i] == str1[j]:
            res[resl] = str1[i]
            res[resr] = str1[j]
            i += 1
            j -= 1
        elif dp[i+1][j] < dp[i][j-1]:
            res[resl] = str1[i]
            res[resr] = str1[i]
            i += 1
        else:
            res[resl] = str1[j]
            res[resr] = str1[j]
            j -= 1
        resl += 1
        resr -= 1
    return ''.join(res)

进阶问题。假设str的长度为N,strlps的长度为M,则整体回文串的长度为2×N - M。整个过程类似 “剥洋葱”。代码如下:

#补充问题
def getPalindrome2(str1, strlps):
    if str1 == None or len(str1) == 0 or strlps == None or len(strlps) == 0:
        return 
    res = [0 for i in range(2*len(str1)-len(strlps))]
    lstr = 0
    rstr = len(str1)-1
    llps = 0
    rlps = len(strlps)-1
    lres = 0
    rres = len(res)-1
    while llps <= rlps:
        temp1 = lstr
        temp2 = rstr
        while str1[lstr] != strlps[llps]:
            lstr += 1
        while str1[rstr] != strlps[rlps]:
            rstr -= 1
        for i in range(temp1, lstr): 
            res[lres] = str1[i]
            res[rres] = str1[i]
            lres += 1
            rres -= 1
        for i in range(temp2, rstr, -1):
            res[lres] = str1[i]
            res[rres] = str1[i]
            lres += 1
            rres -= 1
        res[lres] = str1[lstr]
        res[rres] = str1[rstr]
        lstr += 1
        rstr -= 1
        lres += 1
        rres -= 1
        llps += 1
        rlps -= 1
    return ''.join(res)
  • 3
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值