最长公共子序列问题

问题描述

  一个给定序列的子序列是在该序列中删去若干元素后得到的序列。给定两个序列 A A A B B B找出两个序列的最长公共子序列。

输入格式

  分两行分别输入字符串 A A A B B B

输出格式

  输出分为两行,第一行输出一个整数,表示最长公共子序列的长度,第二行以字符串的形式输出最长公共子序列。

输入样例

gksidertefxsxsepbnwb
sdhfirekdjnfdrwesdcxxhi

输出样例

7
sidrexx

解题思路

  局部的最长公共子序列一定是全局的最长公共子序列的子集,所以局部的最长公共子序列可以被全局的最长公共子序列进行继承或者拓展。
  我们使用 L C S ( P , Q ) LCS(P,Q) LCS(P,Q)表示序列 P P P Q Q Q的最长公共子序列。现在有两个序列 A i = { a 1 , a 2 , a 3 , ⋯   , a i } , B j = { b 1 , b 2 , b 3 , ⋯   , b j } A_i=\{a_1,a_2,a_3,\cdots,a_i\},B_j=\{b_1,b_2,b_3,\cdots,b_j\} Ai={a1,a2,a3,,ai},Bj={b1,b2,b3,,bj},当 a i = b j = s a_i=b_j=s ai=bj=s时,说明 s s s一定是最长公共子序列中的一个元素即 s ∈ L C S ( A i , B j ) s\in LCS(A_i,B_j) sLCS(Ai,Bj),并且是对 L C S ( A i − 1 , B j − 1 ) LCS(A_{i-1},B_{j-1}) LCS(Ai1,Bj1)的一个拓展即 L C S ( A i , B j ) = L C S ( A i − 1 , B j − 1 ) ∪ { s } LCS(A_i,B_j)=LCS(A_{i-1},B_{j-1})\cup\{s\} LCS(Ai,Bj)=LCS(Ai1,Bj1){s};当 a i ≠ b j a_i\not=b_j ai=bj时,说明 L C S ( A i , B j ) LCS(A_i,B_j) LCS(Ai,Bj) L C S ( A i , B j − 1 ) LCS(A_i,B_{j-1}) LCS(Ai,Bj1) L C S ( A i − 1 , B j ) LCS(A_{i-1},B_j) LCS(Ai1,Bj)中即 L C S ( A i , B j ) = max ⁡ ( L C S ( A i , B j − 1 ) , L C S ( A i − 1 , B j ) ) LCS(A_i,B_j)=\max(LCS(A_i,B_{j-1}),LCS(A_{i-1},B_j)) LCS(Ai,Bj)=max(LCS(Ai,Bj1),LCS(Ai1,Bj))
通过上面的推理我们知道此问题满足最优子结构和重叠子问题的性质,因此可以使用动态规划解决。由推论可以得出关于计算最长公共子序列长度的状态转移方程:
t [ i ] [ j ] = { 0 i = 0 0 j = 0 t [ i − 1 ] [ j − 1 ] + 1 a i = b j max ⁡ ( t [ i ] [ j − 1 ] , t [ i − 1 ] [ j ] ) a i ≠ b j t[i][j]=\left\{ \begin{array}{lrl} 0&&i=0\\ 0&&j=0\\ t[i-1][j-1]+1&&a_i=b_j\\ \max(t[i][j-1],t[i-1][j])&&a_i\not=b_j \end{array} \right. t[i][j]=00t[i1][j1]+1max(t[i][j1],t[i1][j])i=0j=0ai=bjai=bj

测试代码

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
    string a,b;
    cin >> a;
    cin >> b;
    vector<vector<size_t> > t(a.size()+1,vector<size_t>(b.size()+1,0));///申请内存空间,并将其中全部填入0
    for(size_t i=0;i<a.size();i++){
        for(size_t j=0;j<b.size();j++){
            if(a[i]==b[j]){///如果匹配则对原最长公共子序列进行拓展
                t[i+1][j+1] = t[i][j]+1;
            }else{///如果不匹配,则在两个序列中找最长的作为当前的最长子公共序列
                t[i+1][j+1] = max(t[i][j+1],t[i+1][j]);
            }
        }
    }
    cout << t[a.size()][b.size()] << endl;
    return 0;
}

#include <iostream>
#include <string>
#include <vector>
#include <stack>
using namespace std;
int main(){
    enum direction{
        up,left,left_up
    };
    string a,b;
    cin >> a;
    cin >> b;
    vector<vector<size_t> > t(a.size()+1,vector<size_t>(b.size()+1));///申请内存空间,并将其中全部填入0
    vector<vector<direction> > s(a.size()+1,vector<direction>(b.size()+1));///申请内存空间,用于记录寻找最长公共子序列的过程
    for(size_t i=0;i<a.size();i++){
        for(size_t j=0;j<b.size();j++){
            if(a[i]==b[j]){///如果匹配则对原最长公共子序列进行拓展
                t[i+1][j+1] = t[i][j]+1;
                s[i+1][j+1] = left_up;
            }else{///如果不匹配,则在两个序列中找最长的作为当前的最长子公共序列
                if(t[i][j+1]<t[i+1][j]){
                    t[i+1][j+1] = t[i+1][j];
                    s[i+1][j+1] = left;
                }else{
                    t[i+1][j+1] = t[i][j+1];
                    s[i+1][j+1] = up;
                }
            }
        }
    }
    cout << t[a.size()][b.size()] << endl;
    stack<char> p;///建立栈
    for(size_t i=a.size(),j=b.size();i&&j;){///从最长公共子序列的最后一个元素开始找起直至找到头部
        switch(s[i][j]){
        case up:
            i--;
            break;
        case left:
            j--;
            break;
        case left_up:
            i--;
            j--;
            p.push(a[i]);///找到匹配项放入栈中
            break;
        }
    }
    while(!p.empty()){///弹栈并打印弹出元素
        cout << p.top();
        p.pop();
    }
    cout << endl;
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值