一.题目:求两个字符序列的最长公共字符子序列。给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。比如字符串1:BDCABA;字符串2:ABCBDAB,则这两个字符串的最长公共子序列长度为4。

二.解法1:递归解法

1.设计思路:分析两个字符串的比较规律,可以发现字符串在进行比较的时候有三种情况:A.str1[i+1]与str2[j]比较;B.str1[i]与str2[j+1]比较;C.str1[i+1]与str2[j+1]比较(两个字符匹配的情况);递归的解法,就是蛮力的去遍历两个字符串比较的所有可能解;如果str1[i]=str2[j]的时候,就需要同时移动两个数组str1[i+1],str2[j+1];如果不相等时,需要考虑去移动str1[i+1]或者str2[j+1],在两个之间选择最大的那一个;根据三种情况去递归,当两个str1[i]=str2[j+1]的时候,就把最大公共子序列的长度+1;

2.代码:

/*最长公共子序列的长度-递归*/
#include<stdio.h>
#include<string.h>
char a[30],b[30];
int lena,lenb;                      //分别保存数组a、b的长度
int LCS(int i,int j);               //两个参数分别表示数组a的下标和数组b的下标int main()
{strcpy(a,"ABCBDAB");strcpy(b,"BDCABA");lena=strlen(a);lenb=strlen(b);printf("最大公共子序列的长度为:%d\n",LCS(0,0));return 0;
}int LCS(int i,int j)
{bool flag=false;if(i>=lena || j>=lenb)return 0;if(a[i]==b[j]){return 1+LCS(i+1,j+1);             //相等时两个数组同时往后移动 }    else                                   //遍历解空间树 {return LCS(i+1,j)>LCS(i,j+1)? LCS(i+1,j):LCS(i,j+1);//遍历找到移动哪一个数组的最大收益 }
}

三.解法2:动态规划

1.设计思路:使用动态规划的思路求解,dp[i][j]数组记录最长公共子序列的长度;自底向上的思考,可以把大的问题,变成小的问题:如果A[i]=B[j],那么有序列的最大长度dp[i][j]=dp[i-1][j-1]+1;如果A[i]!=B[j],那么dp[i][j]=dp[i-1][j]或者dp[i][j]=dp[i][j-1],选择其中最大的一个;两层循环,依次把str1[i]与str2[j]中的每一个元素进行比较,得到以str1[i]为结尾的最大公共子序列的长度;最后输出dp[i][j]最优值,既最大公共子序列的长度。

2.代码:

/*动态规划之最大子序列*/
#include <stdio.h>
int main()
{char A[7]={'A','B','C','B','D','A','B'};            char B[6]={'B','D','C','A','B','A'};int dp[8][7];                      //dp数组记录最长公共子序列的长度 for(int i=0;i<7;i++)       //边界赋值为0 {dp[i][0]=0;}for(int i=0;i<8;i++){dp[0][i]=0;}printf("test1=%d\n",dp[6][7]);for(int i=1;i<=7;i++){for(int j=1;j<=6;j++){if(A[i-1]==B[j-1])          //如果相等就dp[i][j]=dp[i-1][j-1]+1; {dp[i][j]=dp[i-1][j-1]+1;}else{if(dp[i-1][j]>dp[i][j-1]){dp[i][j]=dp[i-1][j];   //取两者之间较大者;局部的最优值 }else{dp[i][j]=dp[i][j-1];}   }}}/*char str[100];                         //记录公共的字符int i=7,j=6;int count=0;while(i>0&&j>0){if(dp[i][j]==dp[i-1][j])            //往上遍历 {i--;}else if(dp[i][j]==dp[i][j-1])        //往左遍历 {j--;}else{str[count++]=A[i-1];i--;j--;}}*/for(int i=0;i<8;i++){for(int j=0;j<7;j++){printf(" %d ",dp[i][j]);}printf("\n");}/*for(int i=count-1;i>=0;i--){printf(" %c ",str[i]);} printf("\n");*/printf("最大子序列的长度为:%d\n",dp[7][6]);      }

四.解法3:改进动态规划(空间压缩)

1.设计思路:由于只是求解最长公共子序列的长度,不需要跟踪最优值的路径,所以可以使用一个一维数组dp[i]去保存以i为结尾的最长公共子序列的长度;在动态规划规划中,dp[i]数组的更新和左边、上边、左对角相关,在使用一维数组的时候,就需要一个pre去存储左对角的值,也就是保存还未改变的dp[i]的值;然后在二维数组中的dp[i][j-1],dp[i-1][j],对应于一维数组中是dp[i-1],dp[i];两层for循环,在内部每一次循环的过程中,首先保存未更新的t=dp[j],然后把对应的值更新到dp[j],同时pre=t,保存为更新的dp[j],也就是二维数组中左对角dp[i-1][j-1]的值;最终输出数组的最后一个数dp[n]即可;

2.代码:

/*最长公共子序列的长度--空间压缩*/
#include <stdio.h>
#define N 6
int main()
{char A[7]={'A','B','C','B','D','A','B'};            char B[6]={'B','D','C','A','B','A'};int dp[N+1];              //dp数组记录最长公共子序列的长度 ,+1方便第一个元素赋值为0 for(int i=0;i<=N;i++)     //边界赋值为0 {dp[i]=0;}for(int i=1;i<=7;i++){int pre=0;                        //保存为更新的dp[j],对应于二维dp[i-1][j-1]的值 for(int j=1;j<=6;j++){int t=dp[j];             if(A[i-1]==B[j-1])            //如果相等就dp[i][j]=dp[i-1][j-1]+1; {dp[j]=pre+1;           //此处pre=dp[i-1][j-1] }else{if(dp[j]>dp[j-1]){dp[j]=dp[j];   //取两者之间较大者;局部的最优值 }else{dp[j]=dp[j-1];}    }pre=t;}}/*char str[100];                          //记录公共的字符int i=7,j=6;int count=0;while(i>0&&j>0){if(dp[i][j]==dp[i-1][j])            //往上遍历 {i--;}else if(dp[i][j]==dp[i][j-1])        //往左遍历 {j--;}else{str[count++]=A[i-1];i--;j--;}}for(int i=0;i<8;i++){for(int j=0;j<7;j++){printf(" %d ",dp[i][j]);}printf("\n");}for(int i=count-1;i>=0;i--){printf(" %c ",str[i]);} printf("\n");*/printf("最大子序列的长度为:%d\n",dp[N]);     }

五.解法4:转换法

1.设计思路:由于两个序列的下标是严格递增的,所以可以把str1中的每一个元素的位置记录下来,求出,str2中每一个元素在str1中出现的位置。此时str2中每个元素在str2中的出现的位置是顺序,然后把每个元素出现的位置序列,进行降序排列。进行降序的目的是防止组成的最长公共子序列中同一个元素重复出现。降序以后,把这些序列组成一个序列,求这个序列的最长递增子序列的长度,就是最长公共子序列的长度。这个过程是把str2在str1中的出现的元素找出来,然后按照一个元素选择一次,并且,递增(因为原来序列的下标是递增的,所以选择的时候,每一个的元素的下标也是递增)。组成的新的最长子序列的长度即是原来两个序列的最长公共子序列的长度。

2.代码:

/*转换发之最长公共子序列的长度*/
#include <stdio.h>
#define N 7
#define M 6
int record[N][M];                   //record[i][j]存储B中元素i在A中出现的位置
int merge[1000];                    //把每一个元素出现的位置降序以后,存储在merge[]中
int main()
{char A[N]={'A','B','C','B','D','A','B'};            char B[M]={'B','D','C','A','B','A'};int temp=0;                   //记录merge[]中实际元素的个数 for(int i=0;i<N;i++)       //求出B中每个元素在A中出现的位置 {int count=0;for(int j=0;j<M;j++){if(B[i]==A[j])           //A中的元素和B中的每一个元素进行比较,是否匹配 {record[i][count++]=j; } }//printf("i=%d\n",i);record[i][count]=-1;}printf("B中每个元素在A中的位置如下:\n");for(int i=0;i<N;i++)         //输出每个元素位置的记录 {int among=0; for(int j=0;j<M&&record[i][j]!=-1;j++){among++;printf(" %d ",record[i][j]);}for(int x=among-1;x>=0;x--)            //把元素的位置降序,并且存储在merge[]中 {merge[temp++]=record[i][x]; }printf("\n");}printf("降序排列的元素位置如下:");for(int i=0;i<temp;i++){printf(" %d ",merge[i]);}printf("\n"); /*接下来使用动态规划求解最长递增子序列的长度*/int dp[100];         //记录以i为起始位置的最长不下降子序列的长度int max;             // 纪录当前元素能够构成的最大不下降子序列的长度int count;         //记录最长不下降子序列的长度和起始位置 for(int i=temp-1;i>=0;i--)    //自底向上的计算每一个元素和后面元素可以构成的最长不下降子序列 {max=0;for(int j=i+1;j<temp;j++) //遍历array[i]后面的其他元素构成的最长不下降子序列的长度{if(merge[i]<merge[j])    //可以构成不下降子序列 {if(dp[j]>max)      //找到当前元素和后面元素构成的最大不下降子序列的长度 {max=dp[j];} } }dp[i]=max+1; //当前元素与后面元素构成的最大不下降子序列的长度需要加上此元素本身}for(int i=0;i<N;i++)       //寻找最大不下降子序列的起始位置和其长度 {if(dp[i]>count){count=dp[i];}}printf("最长公共子序列的长度为:%d\n",count); return 0;
}

六.运行结果:

七.总结:

比较三种解法,发现还是动态规划更加适合解决此题。递归的算法需要去考虑是移动数组一还是移动数组二,而在动态规划的算法中,只需要一次的去比较以当前元素为结尾的最长公共子序列的长度,因为记录了前面元素的比较结果,所以在当前进行比较的时候,可以大大的提高效率;因为此题是求解最优解,而不是最优值,所以可以把动态规划的二维数组,优化为一维数组,可以进一步的节约空间。解法四是一种奇妙的方法,将原来的问题转换成了求最长不下降子序列的长度。

最长公共子序列长度的四种解法相关推荐

  1. 动态规划——最长公共子序列长度

    最长公共子序列长度是编辑距离的另外一种表示方法.只允许添加.删除字符两种惭怍.它表征的是两字符串之间的相似度. 解决思路是: 如果a[i]=b[j],则 公共子序列长度加1,继续考察a[i+1]和b[ ...

  2. 求最长公共子序列长度

    求两个字符串的公共子序列 1.求最长公共子序列(子序列可以不连续) 这是一道动态规划题,设二维数组dp[i][j],dp[i][j]表示a串前i个字符(包括第i个)与b串前j个字符(包括第j个)所有的 ...

  3. python 最长公共子序列长度_python实现最长公共子序列

    最长公共子序列python实现,最长公共子序列是动态规划基本题目,下面按照动态规划基本步骤解出来. 1.找出最优解的性质,并刻划其结构特征 序列a共有m个元素,序列b共有n个元素,如果a[m-1]== ...

  4. 两个字符串的最长公共子序列长度_算法学习笔记(58): 最长公共子序列

    (为什么都更了这么多篇笔记了,这时候才讲这么基础的内容呢?因为我本来以为LCS这种简单的DP不用讲的,结果CF不久前考了LCS的变式,然后我发现由于自己对LCS一点都不熟,居然写不出来 ,于是决定还是 ...

  5. 两个字符串的最长公共子序列长度_【面试】动态规划-之最长公共子序列、最长公共子串问题...

    先来说明下什么是最长公共子序列,什么是是最长公共子串,举一个实际例子,myblogs与belong,最长公共子序列为blog(myblogs, belong),最长公共子串为lo(myblogs, b ...

  6. 两个字符串的最长公共子序列长度_程序员编程算法,解决文本相似度问题的最长公共子序列算法!...

    在前面我讲解了如何通过最长公共子串来求解两个文本的相似度问题,但它有一定缺陷,举个例子,看下面的两个字符串 我爱吃小青菜和各种鲜水果. 我很爱吃青菜与各样水果. 上面两个字符串,如果通过计算子串来求相 ...

  7. 两个字符串的最长公共子序列长度_求2个字符串的最长公共子序列和最长公共子字符串...

    一. 最长公共子序列 定义: 一个数列S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列. 例如:输入两个字符串BDCABA和 ABCBDA ...

  8. 两个字符串的最长公共子序列长度_输出两个字符串的最长公共子串和最长公共子序列...

    输出两个字符串的最长公共子串和最长公共子序列.求解两个字符串的最长公共子串和最长公共子序列在方法上很接近,都是动态规划.只不过在递推方程上有一些不一样. 求两个字符串的最长公共子串 #include ...

  9. LeetCode 5. Longest Palindromic Substring 最长回文子串 Python 四种解法(Manacher 动态规划)

    Longest Palindromic Substring 最长回文子串 学习笔记 1. Brute method 第一种方法:直接循环求解,o(n2)o(n^2) class Solution:de ...

最新文章

  1. linux 可执行文件_linux中ELF二进制程序解析
  2. python复制俩文件夹相同文件_Python比较文件夹比另一同名文件夹多出的文件并复制出来的方法...
  3. matlab盒子分形维数_分形:盒子维数
  4. ajax 如何使 dropdownlist 无刷新,Jquery实现无刷新DropDownList联动实现代码
  5. 如何像阿里巴巴一样高效跨企业项目协作
  6. 8月29日见!卢伟冰:Redmi首款互联网电视将采用70英寸巨屏
  7. Tensorboard可视化具体做法
  8. C++实现Rhino中画准均匀B样条曲线功能
  9. PLC开发没有前景想转行嵌入式,找个培训机构还是自学?
  10. win7激活工具使用
  11. 个人商业模式,如何让自己变得值钱
  12. java nginx 重启吗_nginx(二) : 启动、重启、停止
  13. 【零基础入门学习Python】入门:我和Python的第一次亲密接触
  14. 事业单位计算机技术岗工资,事业单位管理岗、技术岗谁晋升难?到底工资差别有多大?...
  15. 如何将一组列表(三个以上,数值类型不一)保存为txt文件
  16. Linux中文显示乱码问题解决方法 和 将英文提示换成中文提示
  17. 【MATLAB HDF5】Matlab将复数保存到hdf5文件
  18. BP神经网络|函数逼近
  19. stm32f103c8 can控制步科步进电机
  20. 【现代C++】新的字符串格式化方法

热门文章

  1. tring.prototype.replaceAll called with a non-global RegExp argument
  2. Java字符串格式化—String.format()的使用以及Printf的使用
  3. linux里面df的用法,linux系统上df命令的多种用法
  4. android添加图片控件代码,如何在android studio中添加图标图像按钮
  5. 步步高手机预装java游戏_超级圈圈JAVA
  6. 腾讯视频 Python 爬虫项目实战 !
  7. 330tsl是什么意思_途观380、330tsi是什么意思
  8. tez-ui docker镜像打包配置并部署K8S
  9. 计算机网络————网络层
  10. 新版微图APP将于下周内发布