zoukankan      html  css  js  c++  java
  • 动态规划算法2——最长公共子序列

    本文作者frankchenfu,blogs网址http://www.cnblogs.com/frankchenfu/,转载请保留此文字。

    今天,我给大家带来的是“最长公共子序列”(LCS)的讲解。限于水平,这里仅介绍O(nm)算法。

    最长公共子序列其实是很好理解的。

    顾名思义,给出多个(这里暂且只考虑两个)序列,求他们的最长公共子序列,就是在这两个序列中分别删去一些的字符,得到两个相同的序列,使得这两个相同的序列最长。

    当然上面是我自己用比较好理解的方法写的,关于某些“百科”上的解释就是“一个序列S,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则S称为已知序列的最长公共子序列。而最长公共子串(要求连续)和最长公共子序列是不同的 ”。

    这里要先牵扯到一个“子序列”的问题。子序列就是一个序列中,删去一些字符后剩下部分。例如,“abc”就是“axbyc”的一个子序列,他相应的子序列在原序列的下标就是“1,3,5”。

    再比如,

    令字串A为“abcdef”,字串B为“defghi”,那么他们的最长公共子序列是什么呢?

    显然,是“def”。因为子串“d”、“e”和“f”长度均为1,子串“de”、“ef”和“df”均为2,只有“def”长度为3。

    所以,现在,我们给出两个序列,求他们的最长公共子序列。

    【输入格式】

    共两行。每行给出一个长度不超过200的字符串。

    【输出格式】

    共两行。第一行一个非负整数表示最长公共子序列的长度。第二行输出这个子序列(若有多解,任意输出一个)。

    【输入样例】

    ABCBDAB

    BDCABA

    【输出样例】

    4

    BCBA

    【分析】

    看到题目,肯定有人会想到搜索。可是搜索并不能解决一切问题。我们看到搜索是指数级的时间复杂度——你给10秒能不能算完是问题!我们需要设计一个略微高效的算法——尝试使用动态规划。但是在此之前,我们先来看一些有效分析,来验证我们的猜想是否正确。

    (1)最优子结构性质

    这个性质有一个定理,叫LCS的最优子结构性质

    这个定理告诉我们,两个序列的最长公共子序列包含这两个序列的前缀最长公共子序列,所以这个问题是符合最优子结构性质的。

    (2)子问题重叠性质

    由最优子结构性质可以推导出该问题具备子问题重叠性质。我们可以依此建立一个计算到目前为止,子序列“最优值”的问题的递归关系。用c[i][j]记录序列X[i]和Y[i]的最长公共子序列的长度,其中当$i=0$或$j=0$时,$c_{i,j}=0$。否则,当$X_i=Y_i$时,$c_{i,j}=c_{i-1,j-1}+1$,当$X_i$≠$Y_i$时,$c_{i,j}=max(c_{i,j-1},c_{i-1,j})$.

    这些方法得出的时间复杂度已经满足200的数据了,不过可以再优化,将体现在代码中,大家可以自行思考。Cpp代码如下:

    #include<cstdio>
    #include<cstring>
    #include<string>
    using namespace std;
    inline int max(int a,int b)
    {
        return a>b?a:b;
    }
    const int MAX=201;
    int c[MAX][MAX];
    int n,i,j,l1,l2;
    char x[MAX],y[MAX];
    string z="";
    int main()
    {
        scanf("%s%s",x,y);
        l1=strlen(x);l2=strlen(y);
        for(i=1;i<=l1;i++)
            for(j=1;j<=l2;j++)
                if(x[i-1]==y[j-1])
                    c[i][j]=c[i-1][j-1]+1;
                else
                    c[i][j]=max(c[i-1][j],c[i][j-1]);
        printf("%d
    ",c[l1][l2]);
        //求LCS长度已完成,若无需求内容,可忽略以下。 
        for(i=l1,j=l2;i&&j;)
            if(x[i-1]==y[j-1])
            {
                z=x[--i]+z;
                j--;
            }
            else
                if(c[i-1][j]>c[i][j-1])
                    i--;
                else
                    j--;
        printf("%s",z.c_str());//返回C风格可用%s的字符串 
        return 0;
    }

    希望对大家有所帮助,谢谢!

  • 相关阅读:
    学习游戏设计
    AspectJ
    Spring AOP进行日志记录,管理 (使用Spring的拦截器功能获取对action中每个方法的调用情况,在方法调用前和调用后记录相关日志。)
    Java内存泄露测试及工具
    使用Tomcat插件开发WEB应用
    想学习建个网站?WAMP Server助你在Windows上快速搭建PHP集成环境
    UML 基础:类图
    Impala学习--Impala前端代码分析,Impala后端代码分析
    Impala学习--Impala概述,Impala系统架构
    图论--2-SAT--HDU/HDOJ 1814 Peaceful Commission
  • 原文地址:https://www.cnblogs.com/frankchenfu/p/6492528.html
Copyright © 2011-2022 走看看