c语言最长公共子序列_打印出最长的公共子序列(LCS)

本课程详细介绍了如何找出两个序列的最长公共子序列(LCS),并给出了C++实现的算法。在前一课中,讨论了LCS的长度计算,本课在此基础上改进算法,输出具体的LCS序列。通过动态规划方法,构建二维数组L[][],并从右下角开始回溯,得到LCS。同时提出了如何找到所有长度相等的LCS作为思考题。
摘要由CSDN通过智能技术生成

本课程是从少年编程网转载的课程,目标是向中学生详细介绍计算机比赛涉及的编程语言,数据结构和算法。编程学习最好使用计算机,请登陆 www.3dian14.org (免费注册,免费学习)。

在前一课中,我们讨论了最长公共子序列(LCS)问题,并且讨论了如何计算两个序列的LCS的长度,但是没有输出具体的LCS序列。在本课中,我们讨论了如何构造和输出LCS具体序列。

【问题】

给定两个序列,请找出两个序列中存在的最长公共子序列(LCS)。

注意:子序列由原序列中的子元素组成,这些子元素的次序与它们在原序列中出现的次序一样,但不一定连续。而公共子序列是指同时存在于两个已知序列中的子序列,其中长度最长的公共子序列就称为LCS(Longest Common Subsequence)。

【例1】

输入:序列“ABCDGH”和“ AEDFHR”

输出:子序列“ADH”同时出现在这两个序列中,并且是最长的公共子序列,长度为3。

【例2】

输入:序列“AGGTAB”和“ GXTXAYB”

输出:子序列“GTAB”,同时出现在这两个序列中,并且是最长的公共子序列,长度为4。

08095633c55154658fbcf370d608b371.png

算法

我们在上一课输出LCS长度的算法基础上,做进一步的改进,输出具体的LCS内容。

令输入序列分别为长度为m和n的两个数组X[0..m-1]和Y[0..n-1]。

我们使用与上一课中一样定义的二维数组L[][],用来记录序列X和序列Y的LCS的长度。

令L(X[0..m-1], Y[0..n-1]) 为两个序列X和Y的LCS的长度。

L(X[0..m-1], Y[0..n-1]) 的递归定义如下:

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

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

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)然后使用前一课讨论的步骤构造二维数组L[m+1][n+1]。

以X=“AGGTAB",Y="GXTXAYB"为例,得到的L数组如下所示。

8b780536720c3247d777ba8c7abc6451.png

2)值L[m][n]包含LCS的长度。创建一个字符数组lcs[],用来存放找到的LCS序列,lcs数组的长度等于lcs的长度加1(数组最后一个额外的字符用于存储\0,表示字符串的终结)。

3)从L[m][n]开始遍历二维数组。对每个单元格L[i][j]进行跟踪

   a)如果与L[i][j]对应的字符(在X和Y中)相同(或X[i-1] == Y[j-1]),则将此字符作为LCS的一部分 。

   b)否则比较L[i-1][j]和L[i][j-1]的值并朝更大值的方向前进。

lcs的构造过程如下所示。得到的LCS为“GTAB”。

63ac53feddb01ffb09dd99f30d992f9d.png

fabc200aff9875f734d8bf446d8261de.png

算法实现

下面是上述算法的C++实现的一个例子。

#include 

#include 

#include 

using namespace std; 

/* 返回 X[0..m-1], Y[0..n-1]中的LCS长度 */

void lcs( char *X, char *Y, int m, int n ) 

int L[m+1][n+1]; 

/*自底向上构造L[m+1][n+1],L[i][j]包含 X[0..i-1] 和Y[0..j-1]的LCS的长度 

for (int i=0; i<=m; i++) 

for (int 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]); 

//输出LCS

int index = L[m][n]; 

// 数组lcs存放LCS

char lcs[index+1]; 

lcs[index] = '\0'; // 结束字符 

//以右下到左上的次序遍历数组L取出lcs中的字符

int i = m, j = n; 

while (i > 0 && j > 0) 

    //如果X[]和Y[]中的当前字符相等,取出当前字符放入lcs

    if (X[i-1] == Y[j-1]) 

    { 

    lcs[index-1] = X[i-1];  

    i--; j--; index--;

    } 

     //如果X[]和Y[]中的当前字符不相等,向大值方向移动

    else if (L[i-1][j] > L[i][j-1]) 

        i--; 

    else

        j--; 

    } 

    // 输出lcs结果

    cout << "LCS of " << X << " and " << Y << " is " << lcs; 

/* 主程序 */

int main() 

char X[] = "AGGTAB"; 

char Y[] = "GXTXAYB"; 

int m = strlen(X); 

int n = strlen(Y); 

lcs(X, Y, m, n); 

return 0; 

请输入上述程序并验证。

【思考题】上述方法只是找出了一个LCS序列,但是没有输出所有的LCS(因为两个序列X和Y可能有两个或两个以上的长度相等的LCS)。请您思考如何上述算法的基础上,编写程序找出序列X和序列Y的所有的LCS序列。我们将在下一课进行讲解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值