题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串。注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。请编写一个函数,输入两个字符串,求它们的最长公共子序列,并打印出最长公共子序列。
      例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子序列,则输出它们的长度4,并打印任意一个子序列。

分析:求最长公共子序列(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,因此一些重视算法的公司像MicroStrategy都把它当作面试题。

完整介绍动态规划将需要很长的篇幅,因此我不打算在此全面讨论动态规划相关的概念,只集中对LCS直接相关内容作讨论。如果对动态规划不是很熟悉,请参考相关算法书比如算法讨论。

考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bn-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:

(1) 如果am-1==bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;

(2) 如果am-1!=bn-1,则若zk-1!=am-1时,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;

(3) 如果am-1!=bn-1,则若zk-1!=bn-1时,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。

这样,在找A和B的公共子序列时,如果有am-1==bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。

求解:
       引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定输出最长公共字串时搜索的方向。
      我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] == Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。

问题的递归式写成:

回溯输出最长公共子序列过程:

算法分析:
      由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m + n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m + n)。

完整的实现代码如下:

/**
找出两个字符串的最长公共子序列的长度
** author :liuzhiwei
** data   :2011-08-15
**/
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int LCSLength(char* str1, char* str2, int **b)
{int i,j,length1,length2,len;length1 = strlen(str1);length2 = strlen(str2);//双指针的方法申请动态二维数组int **c = new int*[length1+1];      //共有length1+1行for(i = 0; i < length1+1; i++)c[i] = new int[length2+1];      //共有length2+1列for(i = 0; i < length1+1; i++)c[i][0]=0;        //第0列都初始化为0for(j = 0; j < length2+1; j++)c[0][j]=0;        //第0行都初始化为0for(i = 1; i < length1+1; i++){for(j = 1; j < length2+1; j++){if(str1[i-1]==str2[j-1])   //由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-1个元素{c[i][j]=c[i-1][j-1]+1;b[i][j]=0;          //输出公共子串时的搜索方向}else if(c[i-1][j]>c[i][j-1]){c[i][j]=c[i-1][j];b[i][j]=1;}else{c[i][j]=c[i][j-1];b[i][j]=-1;}}}/*for(i= 0; i < length1+1; i++){for(j = 0; j < length2+1; j++)printf("%d ",c[i][j]);printf("\n");}*/len=c[length1][length2];for(i = 0; i < length1+1; i++)    //释放动态申请的二维数组delete[] c[i];delete[] c;return len;
}
void PrintLCS(int **b, char *str1, int i, int j)
{if(i==0 || j==0)return ;if(b[i][j]==0){PrintLCS(b, str1, i-1, j-1);   //从后面开始递归,所以要先递归到子串的前面,然后从前往后开始输出子串printf("%c",str1[i-1]);        //c[][]的第i行元素对应str1的第i-1个元素}else if(b[i][j]==1)PrintLCS(b, str1, i-1, j);elsePrintLCS(b, str1, i, j-1);
}int main(void)
{char str1[100],str2[100];int i,length1,length2,len;printf("请输入第一个字符串:");gets(str1);printf("请输入第二个字符串:");gets(str2);length1 = strlen(str1);length2 = strlen(str2);//双指针的方法申请动态二维数组int **b = new int*[length1+1];for(i= 0; i < length1+1; i++)b[i] = new int[length2+1];len=LCSLength(str1,str2,b);printf("最长公共子序列的长度为:%d\n",len);printf("最长公共子序列为:");PrintLCS(b,str1,length1,length2);printf("\n");for(i = 0; i < length1+1; i++)    //释放动态申请的二维数组delete[] b[i];delete[] b;system("pause");return 0;
}

程序的效果图如下:

第二种方法为:

/**
找出两个字符串的最长公共子序列的长度
** author :liuzhiwei
** data   :2011-08-15
**/
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int LCSLength(char* str1, char* str2)    //求得两个字符串的最大公共子串长度并输出公共子串
{int i,j,length1,length2;length1 = strlen(str1);length2 = strlen(str2);//双指针的方法申请动态二维数组int **c = new int*[length1+1];      //共有length1+1行for(i = 0; i < length1+1; i++)c[i] = new int[length2+1];      //共有length2+1列for(i = 0; i < length1+1; i++)c[i][0]=0;        //第0列都初始化为0for(j = 0; j < length2+1; j++)c[0][j]=0;        //第0行都初始化为0for(i = 1; i < length1+1; i++){for(j = 1; j < length2+1; j++){if(str1[i-1]==str2[j-1])   //由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-1个元素c[i][j]=c[i-1][j-1]+1;else if(c[i-1][j]>c[i][j-1])c[i][j]=c[i-1][j];elsec[i][j]=c[i][j-1];}}//输出公共子串char s[100];int len,k;len=k=c[length1][length2];s[k--]='\0';i=length1,j=length2;while(i>0 && j>0){if(str1[i-1]==str2[j-1]){s[k--]=str1[i-1];i--;j--;}else if(c[i-1][j]<c[i][j-1])j--;elsei--;}printf("最长公共子串为:");puts(s);for(i = 0; i < length1+1; i++)    //释放动态申请的二维数组delete[] c[i];delete[] c;return len;
}int main(void)
{char str1[100],str2[100];int length1,length2,len;printf("请输入第一个字符串:");gets(str1);printf("请输入第二个字符串:");gets(str2);length1 = strlen(str1);length2 = strlen(str2);len=LCSLength(str1,str2);printf("最长公共子串的长度为:%d\n",len);system("pause");return 0;
}

问题拓展:设A、B、C是三个长为n的字符串,它们取自同一常数大小的字母表。设计一个找出三个串的最长公共子序列的O(n^3)的时间算法。
       思路:跟上面的求2个字符串的公共子序列是一样的思路,只不过这里需要动态申请一个三维的数组,三个字符串的尾字符不同的时候,考虑的情况多一些而已。

/**
找出三个字符串的最长公共子序列的长度
** author :liuzhiwei
** data   :2011-08-15
**/
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int max1(int m,int n)
{if(m>n)return m;elsereturn n;
}
int max2(int x,int y,int z,int k,int m,int n)
{int max=-1;if(x>max)max=x;if(y>max)max=y;if(z>max)max=z;if(k>max)max=k;if(m>max)max=m;if(n>max)max=n;return max;
}
int LCSLength(char* str1, char* str2, char* str3)    //求得三个字符串的最大公共子序列长度并输出公共子序列
{int i,j,k,length1,length2,length3,len;length1 = strlen(str1);length2 = strlen(str2);length3 = strlen(str3);//申请动态三维数组int ***c = new int**[length1+1];      //共有length1+1行for(i = 0; i < length1+1; i++){c[i] = new int*[length2+1];      //共有length2+1列for(j = 0; j<length2+1; j++)c[i][j] = new int[length3+1];}for(i = 0; i < length1+1; i++){for(j = 0; j < length2+1; j++)c[i][j][0]=0;}for(i = 0; i < length2+1; i++){for(j = 0; j < length3+1; j++)c[0][i][j]=0;}for(i = 0; i < length1+1; i++){for(j = 0; j < length3+1; j++)c[i][0][j]=0;   }for(i = 1; i < length1+1; i++){for(j = 1; j < length2+1; j++){for(k = 1; k < length3+1; k++){if(str1[i-1]==str2[j-1] && str2[j-1]==str3[k-1])c[i][j][k]=c[i-1][j-1][k-1]+1;else if(str1[i-1]==str2[j-1] && str1[i-1]!=str3[k-1])c[i][j][k]=max1(c[i][j][k-1],c[i-1][j-1][k]);else if(str1[i-1]==str3[k-1] && str1[i-1]!=str2[j-1])c[i][j][k]=max1(c[i][j-1][k],c[i-1][j][k-1]);else if(str2[j-1]==str3[k-1] && str1[i-1]!=str2[j-1])c[i][j][k]=max1(c[i-1][j][k],c[i][j-1][k-1]);else{c[i][j][k]=max2(c[i-1][j][k],c[i][j-1][k],c[i][j][k-1],c[i-1][j-1][k],c[i-1][j][k-1],c[i][j-1][k-1]);}}}}len=c[length1][length2][length3];for(i = 1; i < length1+1; i++)          //释放动态申请的三维数组{for(j = 1; j < length2+1; j++)delete[] c[i][j];delete[] c[i];}delete[] c;return len;
}int main(void)
{char str1[100],str2[100],str3[100];int len;printf("请输入第一个字符串:");gets(str1);printf("请输入第二个字符串:");gets(str2);printf("请输入第三个字符串:");gets(str3);len=LCSLength(str1,str2,str3);printf("最长公共子序列的长度为:%d\n",len);system("pause");return 0;
}

程序的效果图如下:

程序员面试100题之六:最长公共子序列相关推荐

  1. 程序员面试100题之六 最长公共子序列

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴!      ...

  2. 程序员面试100题之七 最长公共子字符串

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 子字符串 ...

  3. 程序员面试100题之十二:求数组中最长递增子序列

    写一个时间复杂度尽可能低的程序,求一个一维数组(N个元素)中最长递增子序列的长度. 例如:在序列1,-1,2,-3,4,-5,6,-7中,其最长递增子序列为1,2,4,6. 分析与解法 根据题目要求, ...

  4. 程序员面试100题之七:最长公共子字符串

    子字符串的定义和子序列的定义类似,但要求是连续分布在其他字符串中.比如输入两个字符串BDCABA和ABCBDAB的最长公共字符串有BD和AB,它们的长度都是2. 最长公共子字符串共有两种解决方法,下面 ...

  5. 程序员面试100题之十五:数组分割

    一.题目概述:有一个没有排序,元素个数为2N的正整数数组.要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近. 假设数组A[1..2N]所有元素的和是SUM.模仿动态规划解0-1背包问题的 ...

  6. 程序员面试100题之十四:强大的和谐

    实现一个挺高级的字符匹配算法: 给一串很长字符串,要求找到符合要求的字符串,例如目的串:123 1******3***2 ,12*****3 这些都要找出来,其实就是类似一些和谐系统..... 这题的 ...

  7. 程序员面试100题之十:快速寻找满足条件的两个数

    能否快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值,为了简化起见,我们假设这个数组中肯定存在至少一组符合要求的解. 假如有如下的两个数组,如图所示: 5,6,1,4,7,9,8 给定S ...

  8. 程序员面试100题之十三:求二叉查找树的镜像

    题目:输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点.用递归和循环两种方法完成树的镜像转换. 例如输入:      8 / \ 6   10 /\  ...

  9. 程序员面试100题之八:不要被阶乘吓倒(二进制表示中最低位1的位置 )

    阶乘(Factorial)是个很有意思的函数,但是不少人都比较怕它,我们来看看两个与阶乘相关的问题: 1. 给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3 628 800, ...

最新文章

  1. 聊聊买卖股票的最佳时机
  2. 阅读《Google成功七堂课》
  3. 网页检测不到java无法打印_如果PC连接到网络打印机,如何检查java?
  4. 打不开/dev/vmmon:断裂管道_湖北加工管道式自卸除铁器厂家询价咨询_国凯环保设备...
  5. SQL Servr 2008空间数据应用系列一:空间信息基础
  6. 云计算底层技术--linux上的虚拟网络设备
  7. 什么是最适合云数据库的架构设计?
  8. MVC中添加动作过滤器的方法
  9. 如何批量导出数据到excel并下载,看这一篇就够了,一看就会
  10. linux下xz格式,【转载】Linux下tar.xz格式文件的解压方法
  11. TCGA肿瘤样本基因信息库(一)
  12. 华硕路由器官改/梅林 设置外置USB-JFFS解救NAND
  13. 项目经理的岗位职责和要求
  14. 漫画:位运算技巧助你俘获offer
  15. 微信朋友圈能评论表情包了,来斗图啊!
  16. 阿里云正式发布小程序Serverless 为用户提供一套代码多端使用
  17. 三星s5 安装android,三星手机怎么安装软件?三星手机安装被阻止情况的解决办法介绍...
  18. 智慧高校共享单车管理系统
  19. 互联网金融带来的十大“挑战”
  20. 一种分布式深度学习编程新范式:Global Tensor

热门文章

  1. opencv java match_OpenCV模板匹配函数matchTemplate详解
  2. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力
  3. 钱大妈数据中台建设最佳实践
  4. 如何通过Graph+AI的方法打造高精度风控模型?
  5. sql server express 并发数的限制_阿里数据库性能诊断的利器——SQL执行干预
  6. SQL Server中的Union和Union All语句之间的差异及其性能
  7. 《苏醒之路》制作人王鲲:独立游戏如何成功出海?
  8. 软件技术专业-就业提示(IT类详细岗位分化)
  9. Mongodb 集群加keyFile认证
  10. BZOJ 3237: [Ahoi2013]连通图