Algorithm --> 最长公共子序列(LCS)

一、什么是最长公共子序列

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

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

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

二、解决方案

<1> 枚举法

这种方法是最简单,也是最容易想到的,当然时间复杂度也是龟速的,我们可以分析一下,刚才也说过了cnblogs的子序列个数有27个 ,延伸一下:一个长度为N的字符串,其子序列有2N个,每个子序列要在第二个长度为N的字符串中去匹配,匹配一次需要O(N)的时间,总共也就是O(N*2N),可以看出,时间复杂度为指数级,恐怖的令人窒息。

<2> 动态规划

最长公共子序列的结构有如下表示:

设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:

1> 若 xm=yn,则 zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列;

2> 若 xm≠yn且 zk≠xm ,则 Z是 Xm-1和 Y的最长公共子序列;

3> 若 xm≠yn且 zk≠yn ,则 Z是 X和 Yn-1的最长公共子序列;

现有两个序列X={x1,x2,x3,...xi},Y={y1,y2,y3,....,yi},设一个C[i,j]: 保存Xi与Yj的LCS的长度,递推方程为:

代码:

    //只能打印一个最长公共子序列
    #include <iostream>
    using namespace std;

     const int X = 100, Y = 100;        //串的最大长度
     char result[X+1];                    //用于保存结果
     int count=0;                        //用于保存公共最长公共子串的个数

     /*功能:计算最优值
      *参数:
      *        x:字符串x
      *        y:字符串y
      *        b:标志数组
      *        xlen:字符串x的长度
      *        ylen:字符串y的长度
      *返回值:最长公共子序列的长度
      *
      */
     int Lcs_Length(string x, string y, int b[][Y+1],int xlen,int ylen)
     {
         int i = 0;
         int j = 0;

         int c[X+1][Y+1];
         for (i = 0; i<=xlen; i++)
         {
             c[i][0]=0;
         }
         for (i = 0; i <= ylen; i++ )
         {
             c[0][i]=0;
         }
         for (i = 1; i <= xlen; i++)
         {
             for (j = 1; j <= ylen; j++)
             {
                 if (x[i - 1] == y[j - 1])
                 {
                     c[i][j] = c[i-1][j-1]+1;
                     b[i][j] = 1;
                 }
                 else if (c[i-1][j] > c[i][j-1])
                 {
                     c[i][j] = c[i-1][j];
                     b[i][j] = 2;
                 }
                 else if(c[i-1][j] <= c[i][j-1])
                 {
                     c[i][j] = c[i][j-1];
                     b[i][j] = 3;
                 }
             }
         }
      return c[xlen][ylen];
     }

    void Display_Lcs(int i, int j, string x, int b[][Y+1],int current_Len)
     {
         if (i ==0 || j==0)
         {
             return;
         }
         if(b[i][j]== 1)
         {
             current_Len--;
             result[current_Len]=x[i- 1];
             Display_Lcs(i-1, j-1, x, b, current_Len);
         }
         else if(b[i][j] == 2)
         {
             Display_Lcs(i-1, j, x, b, current_Len);
         }
         else if(b[i][j]==3)
         {
              Display_Lcs(i, j-1, x, b, current_Len);
         }
         else         {
              Display_Lcs(i-1,j,x,b, current_Len);
         }
     }

     int main(int argc, char* argv[])
     {
         string x = "ABCBDAB";
         string y = "BDCABA";
         int xlen = x.length();
         int ylen = y.length();

         int b[X + 1][Y + 1];

         int lcs_max_len = Lcs_Length( x, y, b, xlen,ylen );
         cout << lcs_max_len << endl;

         Display_Lcs( xlen, ylen, x, b, lcs_max_len );

         //打印结果如下所示
        for(int i = 0; i < lcs_max_len; i++)
        {
            cout << result[i];
        }
        cout << endl;
        return 0;
     }

运行结果如下:

4
BDAB

由于有时并不是只有一个最长公共子序列,所以,对上面的代码进行改进,增加一个数组保存结果等....代码如下所示。

//求取所有的最长公共子序列
    #include <iostream>
    using namespace std;

     const int X = 100, Y = 100;        //串的最大长度
     char result[X+1];                    //用于保存结果
     int cnt = 0;                        //用于保存公共最长公共子串的个数

     /*功能:计算最优值
      *参数:
      *        x:字符串x
      *        y:字符串y
      *        b:标志数组
      *        xlen:字符串x的长度
      *        ylen:字符串y的长度
      *返回值:最长公共子序列的长度
      *
      */
     int Lcs_Length(string x, string y, int b[][Y+1],int xlen,int ylen)
     {
         int i = 0;
         int j = 0;

         int c[X+1][Y+1];
         for (i = 0; i<=xlen; i++)
         {
             c[i][0]=0;
         }
         for (i = 0; i <= ylen; i++ )
         {
             c[0][i]=0;
         }
         for (i = 1; i <= xlen; i++)
         {

             for (j = 1; j <= ylen; j++)
             {
                 if (x[i - 1] == y[j - 1])
                 {
                     c[i][j] = c[i-1][j-1]+1;
                     b[i][j] = 1;
                 }
                 else if (c[i-1][j] > c[i][j-1])
                 {
                     c[i][j] = c[i-1][j];
                     b[i][j] = 2;
                 }
                 else if(c[i-1][j] < c[i][j-1])
                 {
                     c[i][j] = c[i][j-1];
                     b[i][j] = 3;
                 }
                 else
                 {
                     c[i][j] = c[i][j-1]; //或者c[i][j]=c[i-1][j];
                     b[i][j] = 4;
                 }
             }
         }
       return c[xlen][ylen];
     }

     /*功能:计算最长公共子序列
      *参数:
      *        xlen:字符串x的长度
      *        ylen:字符串y的长度
      *        x    :字符串x
      *        b:标志数组
      *        current_len:当前长度
      *        lcs_max_len:最长公共子序列长度
      *
      */
     void Display_Lcs(int i, int j, string x, int b[][Y+1],int current_len,int lcs_max_len)
     {
         if (i ==0 || j==0)
         {
             for(int s=0; s < lcs_max_len; s++)
             {
                 cout << result[s];
             }
             cout<<endl;
             cnt++;
             return;
         }
         if(b[i][j]== 1)
         {
             current_len--;
             result[current_len]=x[i- 1];
             Display_Lcs(i-1, j-1, x, b,current_len,lcs_max_len);
         }
         else if(b[i][j] == 2)
         {
             Display_Lcs(i-1, j, x, b,current_len,lcs_max_len);
         }
         else if(b[i][j]==3)
         {
             Display_Lcs(i, j-1, x, b,current_len,lcs_max_len);
         }
         else
         {
             Display_Lcs(i,j-1,x,b,current_len,lcs_max_len);
             Display_Lcs(i-1,j,x,b,current_len,lcs_max_len);
         }
     }

     int main(int argc, char* argv[])
     {
         string x = "ABCBDAB";
         string y = "BDCABA";
         int xlen = x.length();
         int ylen = y.length();

         int b[X + 1][Y + 1];

         int lcs_max_len = Lcs_Length( x, y, b, xlen,ylen );
         cout << lcs_max_len << endl;

         Display_Lcs( xlen, ylen, x, b, lcs_max_len, lcs_max_len );
         cout << "共有:" << cnt << "种";

         return 0;
     }

运行结果如下:

4
BDAB
BCAB
BCBA
共有: 3 种
时间: 2024-10-15 05:53:58

Algorithm --> 最长公共子序列(LCS)的相关文章

[2016-05-09][51nod][1006 最长公共子序列Lcs]

时间:2016-05-09 21:12:54 星期一 题目编号:[2016-05-09][51nod][1006 最长公共子序列Lcs] 题目大意:[2016-05-09][51nod][1006 最长公共子序列Lcs].md 分析:动态规划 dp[i][j] 表示字符串A以第i个位置 ,字符串B以第j个位置的最长公共子序列的长度 dp[i][j] = dp[i - 1][j - 1] + 1 if a[i] == a[j] else dp[i][j] == max(dp[i - 1][j] ,

POJ 1458 Common Subsequence(最长公共子序列LCS)

POJ1458 Common Subsequence(最长公共子序列LCS) http://poj.org/problem?id=1458 题意: 给你两个字符串, 要你求出两个字符串的最长公共子序列长度. 分析: 本题不用输出子序列,非常easy,直接处理就可以. 首先令dp[i][j]==x表示A串的前i个字符和B串的前j个字符的最长公共子序列长度为x. 初始化: dp全为0. 状态转移: IfA[i]==B[j] then dp[i][j]= dp[i-1][j-1]+1 else dp[

51nod 1006 最长公共子序列Lcs(dp+string,无标记数组实现)

1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 取消关注 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列. Input 第1行:字符串A 第2行:字符串B (A,B的长度 <= 1000) Output 输出最长的子序列,如果有多个,随意输出1个. Input示

编程算法 - 最长公共子序列(LCS) 代码(C)

最长公共子序列(LCS) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 给定两个字符串s,t, 求出这两个字符串最长的公共子序列的长度. 字符串的子序列并一定要连续, 能够包含间隔. 即最长公共子序列问题(LCS, Longest Common Subsequence) 使用动态规划, 假设字符相等, 两个字符串就依次递增一位, 一直到字符串的结尾. 代码: /* * main.cpp * * Created on: 2014.7.17

1006 最长公共子序列Lcs

1006 最长公共子序列Lcs 基准时间限制:1 秒 空间限制:131072 KB 给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列. Input 第1行:字符串A 第2行:字符串B (A,B的长度 <= 1000) Output 输出最长的子序列,如果有多个,随意输出1个. Input示例 abcicba abdkscab Outpu

序列最的问题之最长公共子序列LCS

在程序设计竞赛中,我们时常会遇到序列求最值的问题.在讲今天的问题之前,先小小的说明一下,子序列与子串的问题. 子序列:在原序列中不一定连续: 子串:在原序列中必须连续. 接下来,就开始今天要讲的最长公共子序列LCS(Longest Common Subsequence).对于LCS这一类的问题,一般是相对于两个序列而言,str[]与ch[].先假设str的长度为n,ch的长度为m.假设str[]="ASBDAH",ch[]="SDAAH";其中"SDA&q

动态规划算法解最长公共子序列LCS问题

第一部分.什么是动态规划算法 ok,咱们先来了解下什么是动态规划算法. 动态规划一般也只能应用于有最优子结构的问题.最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能完全满足,故有时需要引入一定的近似).简单地说,问题能够分解成子问题来解决. 动态规划算法分以下4个步骤: 描述最优解的结构 递归定义最优解的值 按自底向上的方式计算最优解的值   //此3步构成动态规划解的基础. 由计算出的结果构造一个最优解.   //此步如果只要求计算最优解的值时,可省略. 好,接下来,咱们

求三个字符串的最长公共子序列LCS(A,B,C)

LCS(A,B,C)!=LCS(A,LCS(B,C)) 反例: abcd abcde abced LCS(B,C)求出来可能是abce或者abcd dp[i][j][k]表示A[0...i],B[0...j],C[0...k]的LCS 转移方程: if (a[i]==b[j]&&b[j]==c[k]) dp[i][j][k]=dp[i-1][j-1][k-1]+1; else dp[i][j][k]=max(max(dp[i][j][k], dp[i-1][j][k]), max(dp[i

动态规划-最长公共子序列LCS

0 问题 给定两个字符串,求最长公共子序列LCS. 也就是说两个字符串中都有的部分,或者理解为,两个字符串同时都删除字符串中的某些字符,使得最终的两个字符串,相等,且是最长的. 1 分析 假设两个str1,str2字符串,已经知道了最长公共子序列长度为L 那么,当在str1和str2,两个的尾部,同时添加一个相同的字符,比如a,那么新的str1,和str2的最长公共子序列长度就是L+1 当str1后面添加一个字符,str2不添加,那么最长公共子序列长度为L 反之,str1不添加,str2添加,那