动态规划学习与实例(2) 最长公共子序列

本文详细解释了最长公共子序列(LCS)问题的解决方法,包括穷举法与动态规划法,以及如何使用C++实现动态规划算法。重点介绍了LCS的最优子结构、递归关系及时间复杂度。

最长公共子序列LCS

( longest common subsequence )

问题描述

  1. 一个给定序列的子序列是该给定序列中去掉0个或者多个元素,此处的子序列不要求必须连续.
  2. 给定序列 XY , 如果 Z 既是 X 的一个子序列,又是 Y 的一个子序列, 则 ZXY 的公共子序列.
  3. 找出给定序列 XY 的最长公共子序列 LCS.

问题分析

  1. 穷举方法:强力枚举出X 的所有子序列,然后逐一检查是否为 Y 的子序列. 假设 Xm个元素,其子序列有 2m 个,需要指数时间.
  2. 动态规划法:LCS 问题具有最优子结构,可以将指数时间化为多项式时间.

LCS 动态规划最优子结构

  1. 假设Zk=<z1,z2,...,zk>XmYn的任意LCS,序列Xm=<x1,x2,...,xm>,Yn=<y1,y2,...,yn>.
  2. 如果xm=yn, 那么zk=xm=yn,且Zk1Xm1Yn1的一个LCS.
  3. 如果xmyn, 则zkxm, ZkXm1Yn的一个LCS.
  4. 如果xmyn, 则zkyn, ZkXmYn1的一个LCS.

LCS 递归关系

定义c[i,j]XiYj的一个LCS的长度.

c[i,j]=0c[i1,j1]+1max{c[i,j1],c[i1,j]}i=0 or j=0i,j>0 and xi=jii,j>0 and xiji

C++代码

#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef vector< vector<int> > mat2;
class Seqmatching
{
        string seq1;
        string seq2;
    public:
        Seqmatching();
        Seqmatching(string seqc1, string seqc2);
        ~Seqmatching();
        void matching(mat2 & seq_n, mat2 & tra) const;
        void lcs(const mat2 & tra, string & subseq, int i, int j) const;
};
Seqmatching::Seqmatching()
{
        seq1 = "";
        seq2 = "";
}
Seqmatching::Seqmatching(string seqc1, string seqc2)
{
        seq1 = seqc1;
        seq2 = seqc2;
}
Seqmatching::~Seqmatching()
{
}
void Seqmatching::matching(mat2 & seq_n, mat2 & tra) const
{
        int n1 = seq1.length();
        int n2 = seq2.length();
        for (int i=1; i<=n1; ++i)
        {
                for (int j=1; j<=n2; ++j)
                {
                        if (seq1.at(i-1) == seq2.at(j-1))
                        {
                                seq_n.at(i).at(j) = seq_n.at(i-1).at(j-1) + 1;
                                tra.at(i).at(j) = 3;    
                        }
                        else
                        {
                                if (seq_n.at(i-1).at(j) >= seq_n.at(i).at(j-1))
                                {
                                        seq_n.at(i).at(j) = seq_n.at(i-1).at(j);
                                        tra.at(i).at(j) = 2;
                                }
                                else
                                {
                                        seq_n.at(i).at(j) = seq_n.at(i).at(j-1);
                                        tra.at(i).at(j) = 1;
                                }
                        }
                }
        }
}
void Seqmatching::lcs(const mat2 & tra, string & subseq, int i, int j) const
{
        subseq.clear();
        if (i!=0 && j!=0)
        {
                if (tra.at(i).at(j) == 3)
                {
                        lcs(tra, subseq, i-1, j-1);
                        subseq += seq1.substr(i-1, 1);
                }
                else
                {
                        if(tra.at(i).at(j) == 2)
                        {
                                lcs(tra, subseq, i-1, j);
                        }
                        else
                        {
                                lcs(tra, subseq, i, j-1);
                        }
                }
        }
}
int main(int argc, char** argv)
{       string seq1(argv[1]);
        string seq2(argv[2]);
        Seqmatching seqm(seq1,seq2);
        int n1 = seq1.length();
        int n2 = seq2.length();
        mat2 seq_n(n1+1, vector<int>(n2+1));
        mat2 tra(n1+1, vector<int>(n2+1));
        seqm.matching(seq_n, tra);
        string subseq;
        seqm.lcs(tra, subseq, n1, n2);          
        cout<<seq1<<endl;
        cout<<seq2<<endl;
        cout<<seq_n.at(n1).at(n2)<<endl;
        cout<<subseq<<endl;
        return 0;
}

分析总结

  1. seq_n[i,j]存储最优子问题的解,tra[i,j]追溯最优解的位置,LCS 最终保存在subseq字符串中.
  2. 该算法的时间复杂度为O(mn). 其中计算最优子问题的时间为O(mn),后续追踪子序列为O(m+n).
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值