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] 。问题可以分解为三个部分:
- 求子串 str[l+1,…….h] 中的最小插入次数。
- 求子串 str[l…….h-1] 中的最小插入次数。
- 求子串 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
#include
#include
// A utility function to find minimum of
// two numbers
int min(int a, int b)
{ return a < b ? a : b; }
// 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";
printf("%d",
findMinInsertions(str, 0,
strlen(str)-1));
return 0;
}
C
// A Dynamic Programming based program to find
// minimum number insertions needed to make a
// string palindrome
#include
#include
// A utility function to find minimum of
// two integers
int min(int a, int b)
{ return a < b ? a : b; }
// 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";
printf("%d",
findMinInsertionsDP(str,
strlen(str)));
return 0;
}
C
// An LCS based program to find minimum number
// insertions needed to make a string palindrome
#include
#include
// Utility function to get max of 2 integers
int max(int a, int b)
{ return (a > b)? a : b; }
/* Returns length of LCS for X[0..m-1], Y[0..n-1].
See http://goo.gl/bHQVP for details of this
function */
int lcs( char *X, char *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];
}
// LCS based function to find minimum number
// of insertions
int findMinInsertionsLCS(char str[], int n)
{
// Creata another string to store reverse
// of 'str'
char rev[n+1];
strcpy(rev, str);
strrev(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()
{
char str[] = "geeks";
printf("%d",
findMinInsertionsLCS(str,
strlen(str)));
return 0;
}
输出:
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
#include
// A utility function to find minimum of
// two integers
int min(int a, int b)
{ return a < b ? a : b; }
// 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";
printf("%d",
findMinInsertionsDP(str,
strlen(str)));
return 0;
}
输出:
3
时间复杂度: O(N^2)
辅助空间: O(N^2)
另一种动态规划解决方案(最长公共子序列问题的变体)
寻找最小插入的问题也可以使用最长公共子序列(LCS)问题来解决。如果我们找出字符串的 LCS 和它的倒数,我们就知道有多少个最大字符可以形成一个回文。我们需要插入剩余的字符。以下是步骤。
- 求输入字符串的 LCS 长度及其倒数。设长度为“l”。
- 所需的最小插入次数是输入字符串的长度减去“l”。
下面是上述方法的实现:
C
// An LCS based program to find minimum number
// insertions needed to make a string palindrome
#include
#include
// Utility function to get max of 2 integers
int max(int a, int b)
{ return (a > b)? a : b; }
/* Returns length of LCS for X[0..m-1], Y[0..n-1].
See http://goo.gl/bHQVP for details of this
function */
int lcs( char *X, char *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];
}
// LCS based function to find minimum number
// of insertions
int findMinInsertionsLCS(char str[], int n)
{
// Creata another string to store reverse
// of 'str'
char rev[n+1];
strcpy(rev, str);
strrev(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()
{
char str[] = "geeks";
printf("%d",
findMinInsertionsLCS(str,
strlen(str)));
return 0;
}
输出:
3
时间复杂度: O(N^2)
辅助空间:O(N^2)
请参阅有关形成回文的最小插入的完整文章 | DP-28 了解更多详情!