Chinaunix首页 | 论坛 | 博客
  • 博客访问: 177015
  • 博文数量: 43
  • 博客积分: 611
  • 博客等级: 中士
  • 技术积分: 1053
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-02 13:37
文章存档

2015年(3)

2013年(23)

2012年(17)

我的朋友

分类: C/C++

2012-12-22 22:43:37

         今天是2012年12月22日。今天的算法练习题是最长公共子序列的长度求解。

         此题初看时,感觉问题非常复杂,要求解两个序列的最长的(可以不连续)的公共子序列。但是,"将复杂的问题分解成简单的问题"是基本的程序设计思想。分治法是将一个大问题分解成多个相似的小问题,而本题采用的动态规划算法,则是将复杂的问题分解成一系列的相似的子问题。另外,将所求解的子问题的解通过数组等容器保存起来,来节省重复求解相同子问题的时间,是动态规划算法的一个很重要的思想:备忘录思想。

         另外,本题还将采用逆向的思路,这与常规的顺序的思路相悖。str1[a],str2[b]两个字符串,不是从左至右的考虑,而是从右至左的方向。

         分析过程如下:

         str1[a],str1[b]是待求最长公共子序列的两个字符串,假设result[c]就是其最长公共子序列。

         那么就有以下3种情况。

         1.如果str1[a-1]==str2[b-1],那么肯定有result[c-1]=str1[a-1]=str2[b-1],即最后一个元素一定是最长公共子序列的一部分。那么,就可以不再考虑str1[a-1],str2[b-1],result[c-1],从他们之前的元素开始讨论,也就是意味着,result[c-2]及之前的元素就是str1[a-2]之前的元素与str[b-2]之前的元素的最长公共子序列。

         2.如果str1[a-1]!=str2[b-1],而且result[c-1]!=str1[a-1],那么就可以肯定,str1[a-1]不在最长公共子序列中,也就是说,result[c-1]及之前的元素就是str1[a-2]之前的元素与str2[b-1]之前的元素的最长公共子序列。

         3.如果str1[a-1]!=str2[b-1],而且result[c-1]!=str2[b-1],类似的,就可以肯定,str1[b-1]不在最长公共子序列中,也就是说,result[c-1]及之前的元素就是str1[a-1]之前的元素与str2[b-2]之前的元素的最长公共子序列。

         由此,可以得到递归方程。(ps:我不知道在这里如何插入公式,如果您知道,请告诉小弟我,非常感谢!)

         max_len[i][j]=0                                      如果i<=0 || j<=0     (当一个序列长度为0时,也就没有意义)

                            =1 +max_len[i-1][j-1]         如果str1[i-1]==str2[j-1]      (对应上面第1种情况)

                            =MAX(max_len[i-1][j],max_len[i][j-1])   如果str1[i-1]!=str2[j-1]     (对应上面第2,3种情况)

         思路已经很清晰了,代码如下:


点击(此处)折叠或打开

  1. 最长公共子序列
  2.  #include <stdio.h>
  3.  #include <string.h>
  4.  #define MAX 100
  5.  
  6.  int max(int num1,int num2);
  7.  
  8.  //记录a[i],b[j]的最长公共子序列的长度
  9.  int max_len[MAX+2][MAX+2]={0};
  10.  
  11.  char a[MAX]="abcde";
  12.  char b[MAX]="afffbcde";
  13.  
  14.  int main()
  15.  {
  16.      int len_a=strlen(a);
  17.      int len_b=strlen(b);
  18.      int result_max=0;
  19.      int i,j;
  20.      LCS(len_a,len_b);
  21.      for(i=1;i<=len_a;i++)
  22.      {
  23.          for(j=1;j<=len_b;j++)
  24.          {
  25.              if(result_max<=max_len[i][j])
  26.                  result_max=max_len[i][j];
  27.          }
  28.      }
  29.      printf("result: %d\n",result_max);
  30.      return 0;
  31.  }
  32.  
  33.  int LCS(int m,int n)
  34.  {
  35.      if(m<=0 || n<=0)
  36.          return 0;
  37.  else if(max_len[m][n]!=0)
  38.         return max_len[m][n];
  39.      else if(a[m-1]==b[n-1])
  40.          return (max_len[m][n]=LCS(m-1,n-1)+1);
  41.      else
  42.          return (max(LCS(m,n-1),LCS(m-1,n)));
  43.  }
  44.  
  45.  int max(int num1,int num2)
  46.  {
  47.      return (num1>num2?num1:num2);
  48.  }
         明天得把具体的最长公共子序列给打印出来。

以下内容补充于2012年12月23日:

          昨天留了个问题,就是打印出最长公共子序列。想了一会,没什么思路,就索性把max_len给打印出来,结果一打印我才发现,我把问题想得太复杂了。这本是一个多么简单的问题。

          比如str1="abbcd";  str2="afffbcd";  那么打印出max_len:

1 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 2 0 0
0 0 0 0 0 3 0
0 0 0 0 0 0 4

  一目了然了。当且仅当str1[a-1]==str2[b-1]时,肯定会使得max_len中对应元素加1.

       在程序中加一步就可以了。如下:


  1. for(i=1;i<=len_a;i++)
  2. {
  3.          for(j=1;j<=len_b;j++)
  4.          {
  5.              if(max_len[i][j]!=0)
  6.                  printf("%c ",a[i-1]);
  7.          }
  8. }

         如果您觉得我的文章对您有所帮助,请顶一下,非常感谢!   

阅读(1187) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~