最长公共子序列(LCS)问题

一、什么是最长公共子序列
   
    什么是最长公共子序列呢?举个简单的例子吧,一个数列S,若分别是两个或多个已知序列的子序列,且是所有符合条件序列中最长的,则S称为已知序列的最长公共子序列。

  举例如下,如:有两个随机数列,1 2 3 4 5 6 和 3 4 5 8 9,则它们的最长公共子序列便是:3 4 5。

  一直不明白:最长公共子串和最长公共子序列的区别。
  
   上网查了下,最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。

二、蛮力法

   蛮力法是解决最长公共子序列问题最容易想到的方法,即对S的每一个子序列,检查是否为T的子序列,从而确定它是否为S和T的公共子序列,并且选出最长的公共子序列。
 
   S和T的所有子序列都检查过后即可求出S和T的最长公共子序列。S的一个子序列相应于下标序列1,2,...,n的一个子序列。因此,S共有2^n个子序列。当然,T也有2^m个子序列。

   因此,蛮力法的时间复杂度为O(2^n * 2^m),这可是指数级别的啊。

三、动态规划方法

   1、序列str1和序列str2
 
  ·长度分别为m和n;
  ·创建1个二维数组L[m.n];
    ·初始化L数组内容为0
    ·m和n分别从0开始,m++,n++循环:
       - 如果str1[m] == str2[n],则L[m,n] = L[m - 1, n -1] + 1;
       - 如果str1[m] != str2[n],则L[m,n] = max{L[m,n - 1],L[m - 1, n]}
    ·最后从L[m,n]中的数字一定是最大的,且这个数字就是最长公共子序列的长度
    ·从数组L中找出一个最长的公共子序列

   2、从数组L中查找一个最长的公共子序列

   i和j分别从m,n开始,递减循环直到i = 0,j = 0。其中,m和n分别为两个串的长度。
  ·如果str1[i] == str2[j],则将str[i]字符插入到子序列内,i--,j--;
  ·如果str1[i] != str[j],则比较L[i,j-1]与L[i-1,j],L[i,j-1]大,则j--,否则i--;(如果相等,则任选一个)

图1 效果演示图
   
   根据上图,我们可以得到其中公共子串:B C B A 和 B D A B。

   总感觉,上面这个过程说的不是很清楚,但是不知道怎么才能更加清楚的表述??纠结啊。

四、代码实现

  1.   //代码实现比较简单,有可能不符合规矩,如有哪位前辈看到后,可以指出,我会虚心学习。
  2.   1 #include <iostream>
  3.   2 #include <string>
  4.   3 using namespace std;
  5.   4 int main(int argc, char **argv)
  6.   5 {
  7.   6  string str1 = "ABCBDAB";
  8.   7  string str2 = "BDCABA";
  9.   8 
  10.   9  int x_len = str1.length();
  11.  10  int y_len = str2.length();
  12.  11 
  13.  12  int arr[50][50] = {{0,0}};
  14.  13 
  15.  14  int i = 0;
  16.  15  int j = 0;
  17.  16 
  18.  17  for(= 1; i <= x_len; i++)
  19.  18  {
  20.  19  for(= 1; j <= y_len; j++)
  21.  20  {
  22.  21  if(str1[- 1] == str2[- 1])
  23.  22  {
  24.  23 arr[i][j] = arr[- 1][- 1] + 1;
  25.  24  }
  26.  25  else
  27.  26  {
  28.  27 
  29.  28  if(arr[i][- 1] >= arr[- 1][j])
  30.  29  {
  31.  30  arr[i][j] = arr[i][- 1];
  32.  31  }
  33.  32  else
  34.  33  {
  35.  34  arr[i][j] = arr[-1][j];
  36.  35  }
  37.  36  }
  38.  37 
  39.  38  }
  40.  39  }
  41. 41     for(i = 0 ; i <= x_len; i++)
  42. 42     {
  43. 43         for( j = 0; j <= y_len; j++)
  44. 44         {
  45. 45             cout << arr[i][j] << "  ";
  46. 46         }
  47. 47         cout << endl;
  48. 48     }
  49. 49     for(i = x_len, j = y_len; i >= 1 && j >= 1;)
  50. 50     {
  51. 51             if(str1[i - 1] == str2[j - 1])
  52. 52             {
  53. 53                 cout << str1[i - 1] << " ";//倒序打印的
  54. 54                 i--;
  55. 55                 j--;
  56. 56             }
  57. 57             else
  58. 58             {
  59. 59             //  if(arr[i][j -1] >= arr[i - 1][j])//打印:B A D B
  60. 60                 if(arr[i][j -1] > arr[i - 1][j]) //打印:A B C B
  61. 61                 {
  62. 62                     j--;
  63. 63                 }
  64. 64                 else
  65. 65                 {
  66. 66                     i--;
  67. 67                 }
  68. 68             }
  69. 69     }
  70. 70     cout << endl;
  71. 71     return 0;
  72. 72 }
  73.  
    运行结果如下所示。
图2 运行效果
   
   最后输出为A B C B,则最大子串为B C B A。
   其实,应该将结果保存起来,然后,正序打印呢。

猜你喜欢

转载自blog.csdn.net/ljyss9/article/details/74276460