您的位置:首页 > 其它

动态规划(篇3)最长公共子序列(LCS)

2017-03-14 15:37 274 查看
LCS问题陈述:

给定两个序列,找出它们中存在的最长子序列的长度。子序列是以相同的相对顺序出现但不一定是连续的序列。例如,“abc”,“abg”,“bdf”,“aeg”,“acefg”,…等是“abcdefg”的子序列。因此,长度为n的串具有2 ^ n个不同的可能子序列。

示例:

用于输入序列的LCS“ABCDGH”和“AEDFHR”是长度为3的“ADH”。

用于输入序列的LCS“AGGTAB”和“GXTXAYB”是长度为4的“GTAB”

这个问题的原始解决方案是生成两个给定序列的所有子序列,并找到最长匹配的子序列。该解在时间复杂度方面是指数的。让我们看看这个问题如何拥有动态规划(DP)问题的两个重要属性。

1)最优子结构:

使输入序列分别为长度m和n的X [0..m-1]和Y [0..n-1]。并且使L(X [0..m-1],Y [0..n-1])是两个序列X和Y的LCS的长度。 m-1],Y [0..n-1])。

如果两个序列的最后字符匹配(或X [m-1] == Y [n-1]),则

L(X [0..m-1],Y [0..n-1])= L(X [0..m-2],Y [0..n-2])

如果两个序列的最后字符不匹配(或X [m-1]!= Y [n-1]),则

L(X [0..m-1],Y [0..n-1] MAX(L(X [0..m-2],Y [0..n-1]),L(X [0..m-1],Y [0..n-2])

示例:

1)考虑输入字符串“AGGTAB”和“GXTXAYB”。字符串的最后字符匹配。因此LCS的长度可以写为:

L(“AGGTAB”,“GXTXAYB”)= 1 + L(“AGGTA”,“GXTXAY”)



2)考虑输入字符串“ABCDGH”和“AEDFHR。最后字符与字符串不匹配。因此LCS的长度可以写为:

L(“ABCDGH”,“AEDFHR”)= MAX(L(“ABCDG”,“AEDFH R ”),L(“ABCDG H ”,“AEDFH”))

因此,LCS问题具有最佳子结构属性,因为主要问题可以使用子问题的解决方案来解决。

2)重叠子问题:

以下是LCS问题的简单递归实现。该实现简单地遵循上述递归结构。

递归代码:

/* A Naive recursive implementation of LCS problem */
#include<bits/stdc++.h>

int max(int a, int b);

/* Returns length of LCS for X[0..m-1], Y[0..n-1] */
int lcs( char *X, char *Y, int m, int n )
{
if (m == 0 || n == 0)
return 0;
if (X[m-1] == Y[n-1])
return 1 + lcs(X, Y, m-1, n-1);
else
return max(lcs(X, Y, m, n-1), lcs(X, Y, m-1, n));
}

/* Utility function to get max of 2 integers */
int max(int a, int b)
{
return (a > b)? a : b;
}

/* Driver program to test above function */
int main()
{
char X[] = "AGGTAB";
char Y[] = "GXTXAYB";

int m = strlen(X);
int n = strlen(Y);

printf("Length of LCS is %d\n", lcs( X, Y, m, n ) );

return 0;
}


输出:

LCS的长度为4


上述递归方法的时间复杂度在最坏情况下为O(2 ^ n),并且当X和Y的所有字符不匹配,即LCS的长度为0时,发生最坏情况。

考虑上述实现,以下是部分递归树输入字符串“AXYT”和“AYZX”

lcs("AXYT", "AYZX")
/                 \
lcs("AXY", "AYZX")            lcs("AXYT", "AYZ")
/            \                  /               \
lcs("AX", "AYZX") lcs("AXY", "AYZ")   lcs("AXY", "AYZ") lcs("AXYT", "AY")


在上述部分递归树中,lcs(“AXY”,“AYZ”)被求解两次。如果我们绘制完整的递归树,那么我们可以看到有很多子问题被一次又一次解决。所以这个问题具有重叠的子结构属性和重新计算相同的子问题可以通过使用记忆或制表避免。以下是LCS问题的列表实现。

动态规划

/* Dynamic Programming C/C++ implementation of LCS problem */
#include<bits/stdc++.h>

int max(int a, int b);

/* Returns length of LCS for X[0..m-1], Y[0..n-1] */
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]
contains length of LCS for X[0..n-1] and Y[0..m-1] */
return L[m]
;
}

/* Utility function to get max of 2 integers */
int max(int a, int b)
{
return (a > b)? a : b;
}

/* Driver program to test above function */
int main()
{
char X[] = "AGGTAB";
char Y[] = "GXTXAYB";

int m = strlen(X);
int n = strlen(Y);

printf("Length of LCS is %d\n", lcs( X, Y, m, n ) );

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息