动态规划专练2 最长公共子序列问题
来源:互联网 发布:苏菲娜护肤品知乎 编辑:程序博客网 时间:2024/06/05 11:17
一、问题描述
求两个字符序列的公共最长子序列。例如字符序列abcbdb和字符序列acbbabdbb的最长公共子序列为acbdb。
假如S1的最后一个元素 与 S2的最后一个元素不等(本例子就是属于这种情况),那么S1和S2的LCS就等于 : {S1减去最后一个元素} 与 S2 的LCS, {S2减去最后一个元素} 与 S1 的LCS 中的最大的那个序列。
四:递推公式:---dp[i][j]=dp[i-1][j-1]+1;(a[i]==b[j])
=max(dp[i][j-1],dp[i-1][j])(a[i]!=b[j])
其中:i,j是指a,b数组中到第i,j个字符的字符串。
五。
解决LCS问题,需要把原问题分解成若干个子问题,所以需要刻画LCS的特征。
设A=“a0,a1,…,am”,B=“b0,b1,…,bn”,且Z=“z0,z1,…,zk”为它们的最长公共子序列。不难证明有以下性质:
如果am=bn,则zk=am=bn,且“z0,z1,…,z(k-1)”是“a0,a1,…,a(m-1)”和“b0,b1,…,b(n-1)”的一个最长公共子序列;
如果am!=bn,则若zk!=am,蕴涵“z0,z1,…,zk”是“a0,a1,…,a(m-1)”和“b0,b1,…,bn”的一个最长公共子序列;
如果am!=bn,则若zk!=bn,蕴涵“z0,z1,…,zk”是“a0,a1,…,am”和“b0,b1,…,b(n-1)”的一个最长公共子序列。
六。代码以及输出最长序列(其中一个)
#include <stdio.h>#include<string.h>char a[1000],b[1000];//输入两个字符串int digit[1000][1000];//记录a到i同时b到j的最大长度int output[1000][1000];//用于输出标记char put[1000];//保存输出字符int main(){ int n,m; while(scanf("%d%d",&n,&m)!=EOF) { getchar(); memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(digit,0,sizeof(digit)); memset(put,0,sizeof(put)); gets(a); gets(b); int i,j; for(i=0; i<n; i++) { for(j=0; j<m; j++) { if(a[i]==b[j]) { digit[i+1][j+1]=digit[i][j]+1;//长度加一 output[i+1][j+1]=1;//此处标记为1 } else if(digit[i+1][j]>=digit[i][j+1])//不然选子序列较大的那个 { digit[i+1][j+1]=digit[i+1][j]; output[i+1][j+1]=2;//删掉b结尾较大,此处标记为2 } else { digit[i+1][j+1]=digit[i][j+1];//删掉a结尾较大,此处标记为3 output[i+1][j+1]=3; } } } printf("最长公共子序列长度为: %d\n",digit[n][m]); i=n,j=m; int k=digit[n][m]; while(i>0&&j>0) { if(output[i][j]==1)//==1时为两子序列公共地方,存下来 { put[k-1]=a[i-1]; i--; j--; k--; } else if(output[i][j]==2)//标记为2时是删掉b结尾导致的,顺着往上应该j--,下面同理 j--; else i--; } printf("最长公共子序列为: %s\n",put); } return 0;}