📜  Python程序查找最小插入以形成回文| DP-28

📅  最后修改于: 2022-05-13 01:56:04.484000             🧑  作者: Mango

Python程序查找最小插入以形成回文| DP-28

给定字符串str ,任务是找到要插入的最小字符数以将其转换为回文。

在我们继续之前,让我们通过几个例子来理解:

  • ab:所需的插入次数为 1 即b ab
  • aa:所需的插入次数为 0,即 aa
  • abcd:所需的插入次数为 3 即dcb abcd
  • abcda:所需的插入次数为 2,即dc bcda,它与子串 bcd 中的插入次数相同(为什么?)。
  • abcde:所需的插入次数为 4 即edcb abcde

让输入字符串为str[l……h] 。问题可以分解为三个部分:

  1. 求子串 str[l+1,…….h] 中的最小插入次数。
  2. 求子串 str[l…….h-1] 中的最小插入次数。
  3. 求子串 str[l+1……h-1] 中的最小插入次数。

递归方法:字符串str[l.....h]中的最小插入次数可以给出为:

  • minInsertions(str[l+1…..h-1]) 如果 str[l] 等于 str[h]
  • min(minInsertions(str[l…..h-1]), minInsertions(str[l+1…..h])) + 1 否则

下面是上述方法的实现:

Python 3
# A Naive recursive program to find
# minimum number insertions needed
# to make a string palindrome
import sys
 
# Recursive function to find
# minimum number of insertions
def findMinInsertions(str, l, h):
 
    # Base Cases
    if (l > h):
        return sys.maxsize
    if (l == h):
        return 0
    if (l == h - 1):
        return 0 if(str[l] == str[h]) else 1
 
    # Check if the first and last characters
    # are same. On the basis of the comparison
    # result, decide which subrpoblem(s) to call
     
    if(str[l] == str[h]):
        return findMinInsertions(str,
                                 l + 1, h - 1)
    else:
        return (min(findMinInsertions(str,
                                      l, h - 1),
                    findMinInsertions(str,
                                      l + 1, h)) + 1)
 
# Driver Code
if __name__ == "__main__":
     
    str = "geeks"
    print(findMinInsertions(str, 0, len(str) - 1))
 
# This code is contributed by ita_c


Python3
# A Dynamic Programming based program to
# find minimum number insertions needed
# to make a string palindrome
 
# A utility function to find minimum
# of two integers
def Min(a, b):
    return min(a, b)
 
# A DP function to find minimum number
# of insertions
def findMinInsertionsDP(str1, n):
 
    # Create a table of size n*n. table[i][j]
    # will store minimum number of insertions
    # needed to convert str1[i..j] to a
    # palindrome.
    table = [[0 for i in range(n)]
                for i in range(n)]
    l, h, gap = 0, 0, 0
 
    # Fill the table
    for gap in range(1, n):
        l = 0
        for h in range(gap, n):
            if str1[l] == str1[h]:
                table[l][h] = table[l + 1][h - 1]
            else:
                table[l][h] = (Min(table[l][h - 1],
                                   table[l + 1][h]) + 1)
            l += 1
 
    # Return minimum number of insertions
    # for str1[0..n-1]
    return table[0][n - 1];
 
# Driver Code
str1 = "geeks"
print(findMinInsertionsDP(str1, len(str1)))
 
# This code is contributed by Mohit kumar 29


Python3
# An LCS based Python3 program to find minimum
# number insertions needed to make a string
# palindrome
 
""" Returns length of LCS for X[0..m-1],
Y[0..n-1]. See http://goo.gl/bHQVP for
details of this function """
def lcs(X, Y, m, n) :
 
    L = [[0 for i in range(n + 1)] for j in range(m + 1)]
 
    """ Following steps build L[m + 1, n + 1] in
    bottom up fashion. Note that L[i, j]
    contains length of LCS of X[0..i - 1]
    and Y[0..j - 1] """
    for i in range(m + 1) :    
        for j in range(n + 1) :      
            if (i == 0 or j == 0) :
                L[i][j] = 0
 
            elif (X[i - 1] == Y[j - 1]) :
                L[i][j] = L[i - 1][j - 1] + 1
            else :
                L[i][j] = max(L[i - 1][j], L[i][j - 1])
 
    """ L[m,n] contains length of LCS for
    X[0..n-1] and Y[0..m-1] """
    return L[m][n]
     
# LCS based function to find minimum number
# of insertions
def findMinInsertionsLCS(Str, n) :
 
    # Using charArray to reverse a String
    charArray = list(Str)
    charArray.reverse()
    revString = "".join(charArray)
     
    # The output is length of string minus
    # length of lcs of str and it reverse
    return (n - lcs(Str, revString , n, n))
 
# Driver code 
Str = "geeks"
print(findMinInsertionsLCS(Str,len(Str)))
# This code is contributed by divyehrabadiya07


输出:

3

基于动态规划的解决方案
如果我们仔细观察上述方法,我们会发现它表现出重叠的子问题。
假设我们想找到字符串“abcde”中的最小插入次数:

abcde
            /       |      
           /        |        
           bcde         abcd       bcd  <- case 3 is discarded as str[l] != str[h]
       /   |          /   |   
      /    |         /    |    
     cde   bcd  cd   bcd abc bc
   / |   / |  /| / | 
de cd d cd bc c………………….

粗体子串表示递归将被终止,递归树不能从那里开始。相同颜色的子字符串表示重叠的子问题。

如何重用子问题的解决方案?记忆技术用于避免类似的子问题召回。我们可以创建一个表来存储子问题的结果,以便在再次遇到相同的子问题时可以直接使用它们。
下表表示字符串abcde 的存储值。

a b c d e
----------
0 1 2 3 4
0 0 1 2 3 
0 0 0 1 2 
0 0 0 0 1 
0 0 0 0 0

如何填表?
表格应以对角线方式填充。对于字符串abcde, 0….4,应按以下顺序填充表格:

Gap = 1: (0, 1) (1, 2) (2, 3) (3, 4)

Gap = 2: (0, 2) (1, 3) (2, 4)

Gap = 3: (0, 3) (1, 4)

Gap = 4: (0, 4)

下面是上述方法的实现:

Python3

# A Dynamic Programming based program to
# find minimum number insertions needed
# to make a string palindrome
 
# A utility function to find minimum
# of two integers
def Min(a, b):
    return min(a, b)
 
# A DP function to find minimum number
# of insertions
def findMinInsertionsDP(str1, n):
 
    # Create a table of size n*n. table[i][j]
    # will store minimum number of insertions
    # needed to convert str1[i..j] to a
    # palindrome.
    table = [[0 for i in range(n)]
                for i in range(n)]
    l, h, gap = 0, 0, 0
 
    # Fill the table
    for gap in range(1, n):
        l = 0
        for h in range(gap, n):
            if str1[l] == str1[h]:
                table[l][h] = table[l + 1][h - 1]
            else:
                table[l][h] = (Min(table[l][h - 1],
                                   table[l + 1][h]) + 1)
            l += 1
 
    # Return minimum number of insertions
    # for str1[0..n-1]
    return table[0][n - 1];
 
# Driver Code
str1 = "geeks"
print(findMinInsertionsDP(str1, len(str1)))
 
# This code is contributed by Mohit kumar 29

输出:

3

时间复杂度: O(N^2)
辅助空间: O(N^2)

另一种动态规划解决方案(最长公共子序列问题的变体)
寻找最小插入的问题也可以使用最长公共子序列(LCS)问题来解决。如果我们找出字符串的 LCS 和它的倒数,我们就知道有多少个最大字符可以形成一个回文。我们需要插入剩余的字符。以下是步骤。

  1. 求输入字符串的 LCS 长度及其倒数。设长度为“l”。
  2. 所需的最小插入次数是输入字符串的长度减去“l”。

下面是上述方法的实现:

Python3

# An LCS based Python3 program to find minimum
# number insertions needed to make a string
# palindrome
 
""" Returns length of LCS for X[0..m-1],
Y[0..n-1]. See http://goo.gl/bHQVP for
details of this function """
def lcs(X, Y, m, n) :
 
    L = [[0 for i in range(n + 1)] for j in range(m + 1)]
 
    """ Following steps build L[m + 1, n + 1] in
    bottom up fashion. Note that L[i, j]
    contains length of LCS of X[0..i - 1]
    and Y[0..j - 1] """
    for i in range(m + 1) :    
        for j in range(n + 1) :      
            if (i == 0 or j == 0) :
                L[i][j] = 0
 
            elif (X[i - 1] == Y[j - 1]) :
                L[i][j] = L[i - 1][j - 1] + 1
            else :
                L[i][j] = max(L[i - 1][j], L[i][j - 1])
 
    """ L[m,n] contains length of LCS for
    X[0..n-1] and Y[0..m-1] """
    return L[m][n]
     
# LCS based function to find minimum number
# of insertions
def findMinInsertionsLCS(Str, n) :
 
    # Using charArray to reverse a String
    charArray = list(Str)
    charArray.reverse()
    revString = "".join(charArray)
     
    # The output is length of string minus
    # length of lcs of str and it reverse
    return (n - lcs(Str, revString , n, n))
 
# Driver code 
Str = "geeks"
print(findMinInsertionsLCS(Str,len(Str)))
# This code is contributed by divyehrabadiya07

输出:

3

时间复杂度: O(N^2)
辅助空间:O(N^2)

请参阅有关形成回文的最小插入的完整文章 | DP-28 了解更多详情!