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

来源:互联网 发布:高斯模糊算法 cpu消耗 编辑:程序博客网 时间:2024/05/22 06:10

【题目】

  给定一个字符串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)
阅读全文
2 0