📜  C++ 程序寻找最小插入以形成回文| DP-28

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

C++ 程序寻找最小插入以形成回文| 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 否则

下面是上述方法的实现:

C++
// A Naive recursive program to find minimum 
// number insertions needed to make a string
// palindrome
#include
using namespace std;
  
  
// Recursive function to find  
// minimum number of insertions
int findMinInsertions(char str[], 
                      int l, int h)
{
    // Base Cases
    if (l > h) 
        return INT_MAX;
  
    if (l == h) 
        return 0;
  
    if (l == h - 1) 
        return (str[l] == str[h]) ? 0 : 1;
  
    // Check if the first and last characters are
    // same. On the basis of the comparison result, 
    // decide which subrpoblem(s) to call
    return (str[l] == str[h])? 
            findMinInsertions(str, l + 1, h - 1):
            (min(findMinInsertions(str, l, h - 1),
             findMinInsertions(str, l + 1, h)) + 1);
}
  
// Driver code
int main()
{
    char str[] = "geeks";
    cout << findMinInsertions(str, 0, 
                              strlen(str) - 1);
    return 0;
}
// This code is contributed by Akanksha Rai


C++
// A Dynamic Programming based program to find 
// minimum number insertions needed to make a 
// string palindrome 
#include 
using namespace std;
  
  
// A DP function to find minimum
// number of insertions 
int findMinInsertionsDP(char str[], int n) 
{ 
    // Create a table of size n*n. table[i][j] 
    // will store minimum number of insertions 
    // needed to convert str[i..j] to a palindrome. 
    int table[n][n], l, h, gap; 
  
    // Initialize all table entries as 0 
    memset(table, 0, sizeof(table)); 
  
    // Fill the table 
    for (gap = 1; gap < n; ++gap) 
        for (l = 0, h = gap; h < n; ++l, ++h) 
            table[l][h] = (str[l] == str[h])? 
                           table[l + 1][h - 1] : 
                           (min(table[l][h - 1], 
                            table[l + 1][h]) + 1); 
  
    // Return minimum number of insertions
    // for str[0..n-1] 
    return table[0][n - 1]; 
} 
  
// Driver Code
int main() 
{ 
    char str[] = "geeks"; 
    cout << findMinInsertionsDP(str, 
                                strlen(str)); 
    return 0; 
} 
// This is code is contributed by rathbhupendra


C++
// An LCS based program to find minimum number 
// insertions needed to make a string palindrome 
#include 
using namespace std;
   
// Returns length of LCS for X[0..m-1], 
// Y[0..n-1]. 
int lcs(string X, string Y, 
        int m, int n) 
{ 
    int L[m+1][n+1]; 
    int i, j; 
      
    /* 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 = 0; i <= m; i++) 
    { 
        for (j = 0; j <= n; j++) 
        { 
        if (i == 0 || j == 0) 
            L[i][j] = 0; 
      
        else if (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]; 
} 
  
void reverseStr(string& str) 
{ 
    int n = str.length(); 
  
    // Swap character starting from two 
    // corners 
    for (int i = 0; i < n / 2; i++) 
        swap(str[i], str[n - i - 1]); 
} 
  
// LCS based function to find minimum 
// number of insertions 
int findMinInsertionsLCS(string str, int n) 
{ 
    // Creata another string to store 
    // reverse of 'str' 
    string rev = ""; 
    rev = str; 
    reverseStr(rev); 
      
    // The output is length of string minus 
    // length of lcs of str and it reverse 
    return (n - lcs(str, rev, n, n)); 
} 
  
// Driver code
int main() 
{ 
    string str = "geeks"; 
    cout << findMinInsertionsLCS(str, 
                                 str.length()); 
    return 0; 
} 
// This code is contributed by rathbhupendra


输出:

3

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

abcde
            /       |      
           /        |        
           bcde         abcd       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)

下面是上述方法的实现:

C++

// A Dynamic Programming based program to find 
// minimum number insertions needed to make a 
// string palindrome 
#include 
using namespace std;
  
  
// A DP function to find minimum
// number of insertions 
int findMinInsertionsDP(char str[], int n) 
{ 
    // Create a table of size n*n. table[i][j] 
    // will store minimum number of insertions 
    // needed to convert str[i..j] to a palindrome. 
    int table[n][n], l, h, gap; 
  
    // Initialize all table entries as 0 
    memset(table, 0, sizeof(table)); 
  
    // Fill the table 
    for (gap = 1; gap < n; ++gap) 
        for (l = 0, h = gap; h < n; ++l, ++h) 
            table[l][h] = (str[l] == str[h])? 
                           table[l + 1][h - 1] : 
                           (min(table[l][h - 1], 
                            table[l + 1][h]) + 1); 
  
    // Return minimum number of insertions
    // for str[0..n-1] 
    return table[0][n - 1]; 
} 
  
// Driver Code
int main() 
{ 
    char str[] = "geeks"; 
    cout << findMinInsertionsDP(str, 
                                strlen(str)); 
    return 0; 
} 
// This is code is contributed by rathbhupendra

输出:

3

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

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

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

下面是上述方法的实现:

C++

// An LCS based program to find minimum number 
// insertions needed to make a string palindrome 
#include 
using namespace std;
   
// Returns length of LCS for X[0..m-1], 
// Y[0..n-1]. 
int lcs(string X, string Y, 
        int m, int n) 
{ 
    int L[m+1][n+1]; 
    int i, j; 
      
    /* 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 = 0; i <= m; i++) 
    { 
        for (j = 0; j <= n; j++) 
        { 
        if (i == 0 || j == 0) 
            L[i][j] = 0; 
      
        else if (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]; 
} 
  
void reverseStr(string& str) 
{ 
    int n = str.length(); 
  
    // Swap character starting from two 
    // corners 
    for (int i = 0; i < n / 2; i++) 
        swap(str[i], str[n - i - 1]); 
} 
  
// LCS based function to find minimum 
// number of insertions 
int findMinInsertionsLCS(string str, int n) 
{ 
    // Creata another string to store 
    // reverse of 'str' 
    string rev = ""; 
    rev = str; 
    reverseStr(rev); 
      
    // The output is length of string minus 
    // length of lcs of str and it reverse 
    return (n - lcs(str, rev, n, n)); 
} 
  
// Driver code
int main() 
{ 
    string str = "geeks"; 
    cout << findMinInsertionsLCS(str, 
                                 str.length()); 
    return 0; 
} 
// This code is contributed by rathbhupendra

输出:

3

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

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