最长公共子序列

来源:互联网 发布:中文域名不流行 编辑:程序博客网 时间:2024/06/06 00:38
 题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串。注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。请编写一个函数,输入两个字符串,求它们的最长公共子序列,并打印出最长公共子序列。
      例如:输入两个字符串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)。

      完整的实现代码如下:

[cpp] view plain copy
  1. /**  
  2. 找出两个字符串的最长公共子序列的长度 
  3. ** author :liuzhiwei   
  4. ** data   :2011-08-15 
  5. **/   
  6. #include "stdio.h"  
  7. #include "string.h"  
  8. #include "stdlib.h"  
  9. int LCSLength(char* str1, char* str2, int **b)  
  10. {  
  11.     int i,j,length1,length2,len;  
  12.     length1 = strlen(str1);  
  13.     length2 = strlen(str2);  
  14.   
  15.     //双指针的方法申请动态二维数组  
  16.     int **c = new int*[length1+1];      //共有length1+1行  
  17.     for(i = 0; i < length1+1; i++)  
  18.         c[i] = new int[length2+1];      //共有length2+1列  
  19.   
  20.     for(i = 0; i < length1+1; i++)  
  21.         c[i][0]=0;        //第0列都初始化为0  
  22.     for(j = 0; j < length2+1; j++)  
  23.         c[0][j]=0;        //第0行都初始化为0  
  24.   
  25.     for(i = 1; i < length1+1; i++)  
  26.     {  
  27.         for(j = 1; j < length2+1; j++)  
  28.         {  
  29.             if(str1[i-1]==str2[j-1])   //由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-1个元素  
  30.             {  
  31.                 c[i][j]=c[i-1][j-1]+1;  
  32.                 b[i][j]=0;          //输出公共子串时的搜索方向  
  33.             }  
  34.             else if(c[i-1][j]>c[i][j-1])  
  35.             {  
  36.                 c[i][j]=c[i-1][j];  
  37.                 b[i][j]=1;  
  38.             }  
  39.             else  
  40.             {  
  41.                 c[i][j]=c[i][j-1];  
  42.                 b[i][j]=-1;  
  43.             }  
  44.         }  
  45.     }  
  46.     /* 
  47.     for(i= 0; i < length1+1; i++) 
  48.     { 
  49.     for(j = 0; j < length2+1; j++) 
  50.     printf("%d ",c[i][j]); 
  51.     printf("\n"); 
  52.     } 
  53.     */  
  54.     len=c[length1][length2];  
  55.     for(i = 0; i < length1+1; i++)    //释放动态申请的二维数组  
  56.         delete[] c[i];  
  57.     delete[] c;  
  58.     return len;  
  59. }  
  60. void PrintLCS(int **b, char *str1, int i, int j)  
  61. {  
  62.     if(i==0 || j==0)  
  63.         return ;  
  64.     if(b[i][j]==0)  
  65.     {  
  66.         PrintLCS(b, str1, i-1, j-1);   //从后面开始递归,所以要先递归到子串的前面,然后从前往后开始输出子串  
  67.         printf("%c",str1[i-1]);        //c[][]的第i行元素对应str1的第i-1个元素  
  68.     }  
  69.     else if(b[i][j]==1)  
  70.         PrintLCS(b, str1, i-1, j);  
  71.     else  
  72.         PrintLCS(b, str1, i, j-1);  
  73. }  
  74.   
  75. int main(void)  
  76. {  
  77.     char str1[100],str2[100];  
  78.     int i,length1,length2,len;  
  79.     printf("请输入第一个字符串:");  
  80.     gets(str1);  
  81.     printf("请输入第二个字符串:");  
  82.     gets(str2);  
  83.     length1 = strlen(str1);  
  84.     length2 = strlen(str2);  
  85.     //双指针的方法申请动态二维数组  
  86.     int **b = new int*[length1+1];  
  87.     for(i= 0; i < length1+1; i++)  
  88.         b[i] = new int[length2+1];  
  89.     len=LCSLength(str1,str2,b);  
  90.     printf("最长公共子序列的长度为:%d\n",len);  
  91.     printf("最长公共子序列为:");  
  92.     PrintLCS(b,str1,length1,length2);  
  93.     printf("\n");  
  94.     for(i = 0; i < length1+1; i++)    //释放动态申请的二维数组  
  95.         delete[] b[i];  
  96.     delete[] b;  
  97.     system("pause");  
  98.     return 0;  
  99. }  

          程序的效果图如下:


    

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 金龙和银龙打架怎么办 海水缸盐度高了怎么办 洗空调洗坏了怎么办 老师是条青花鱼怎么办 吃了带鱼和南瓜怎么办 苹果平板ad忘了怎么办 小米6进海水了怎么办 小米手机掉海水里怎么办 苹果7进海水了怎么办 7p手机进海水怎么办 育海参苗出现红细菌怎么办 苹果手机掉进厕所怎么办 2个月的婴儿肺炎怎么办 甜甜圈珊瑚脱骨怎么办 宝宝吃了生物球怎么办 狗尾巴被剪掉了怎么办 魟鱼尾巴刺了怎么办 狗咬过了24小时怎么办 狗咬超过24小时怎么办 狗咬超过48小时怎么办 狗咬超过72小时怎么办 狗抓超过24小时怎么办 龙须树叶子发黄怎么办? 车被广告牌砸了怎么办 开花店压力好大怎么办 快成熟葡萄软果怎么办 木本叶长白斑点了怎么办 天猫卖家超过72小时未发货怎么办 淘宝超过72小时不发货怎么办 鸿运当头干叶了怎么办 百合枝干长歪了怎么办 植物主干长歪了怎么办 钱兜子花叶子黄怎么办 丝瓜有苦味吃了怎么办 吃了苦的西葫芦怎么办 吃了苦的西葫芦中毒怎么办 吃了一点苦丝瓜怎么办 丝瓜苦味我吃了怎么办? 小孩牙地包天怎么办 小孩的牙地包天怎么办 小孩牙是地包天怎么办