动态规划之最长公共子序列

首先需要说一下的是,什么是公共子序列。子串的概念就是,一个字符串中连续的一部分, 而 子序列,可以是不连续的。

•一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
•给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
•最长公共子序列:公共子序列中长度最长的子序列。
【穷举法】
•对于每一个Xm的子序列,验证它是否是Yn的子序列.
•Xm有2m个子序列
•每个子序列需要o(n)的时间来验证它是否是Yn的子序列.
–从Yn的第一个字母开始扫描下去,如果不是则从第二个开始
•运行时间: o(2^n*2^m)
 
 
【动态规划】
•两个字符串【横向,纵向排列】
ABDTFGHFEABADG
BTHEBDCEFG
   A B DT F G H F EA B A D G
B 0 1 1 1 1 1 1 1 1 1 1 1 1 1
T 0 1 1 2 2 2 2 2 2 2 2 2 2 2
H 0 1 1 2 2 2 3 3 3 3 3 3 3 3
E 0 1 1 2 2 2 3 3 4 4 4 4 4 4
B 0 1 1 2 2 2 3 3 4 4 5 5 5 5
D 0 1 2 2 2 2 3 3 4 4 5 5 6 6
C 0 1 2 2 2 2 3 3 4 4 5 5 6 6
E 0 1 2 2 2 2 3 3 4 4 5 5 6 6
F 0 1 2 2 3 3 3 4 4 4 5 5 6 6
G 0 1 2 2 3 4 4 4 4 4 5 5 6 7

状态迁移方程:

c[i][j] 是用来保存截止到X[i],Y[j]的时候的最长公共子序列的长度。

c[i][j] =0,                                  i = j =0;

c[i][j] = c[i-1][j-1]+1 ,               i>0,j>0 && Xi = Yj

c[i][j] = max(c[i-1][j],c[i][j-1]),  i>0,j>0 && Xi ≠ Yj

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 1000;
string str1,str2;
int c[maxn][maxn];
int n;int m;
void solve()
{

    for(int i = 0;i<n;i++)
    {

        for(int j = 0;j<m;j++)
        {
            if(str1[i] ==str2[j])
                c[i+1][j+1] = c[i][j] + 1;
           else
           c[i+1][j+1] = max(c[i+1][j],c[i][j+1]);
        }
    }

}

int main()
{
    cin>>str1>>str2;
     n = str1.length();
     m = str2.length();
    solve();
    cout<<c[n][m]<<endl;

    return 0;
}

 后来仔细想想,没那么复杂其实也,只要自己肯动脑子……

昨天晚上那个心急啊,想想该如何输出其中的一个最大公共子序列呢……

矩阵 b[i, j]:存放当前的路径,也就是说c[i][j]是如何得来的。

如果c[i][j] = c[i-1][j-1] 也就是对角线的关系;b[i][j] 标记为 '↖' b[i][j]  = 'S';

如果c[i][j] = c[i][j-1] 也就是左右的关系 ,b[i][j] 标记为 '←' b[i][j]  = 'L';

如果c[i][j] = c[i-1][j] 也就是上下的关系,b[i][j] 标记为 '↑' b[i][j]  = 'U';

 例如:

  B D C A B A

A U U U S L S
B S  L L U S L
C U U S L U U
B S U U U S L
D U S U U U U
A U U U S U S
B S U U U S U

从最后一个开始查找 U→S→U→S→L→S→L→S其中S对应的str1的元素就是其中的一条最长公共子子序列。

使用递归的办法,遇到一个S就压入栈……

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 1000;
string str1,str2;
int c[maxn][maxn];
char b[maxn][maxn];
int n;int m;
void printLCS(int i, int j)

{

    if(i==0 || j==0)

        return;

    if(b[i][j] == 'S')

    {

        printLCS(i-1, j-1);

        cout<<str1[i-1]<<' ';

    }

    else

    {

        if(b[i][j] == 'U')

            printLCS(i-1, j);

        else

            printLCS(i, j-1);

    }

}
void solve()
{

    for(int i = 0;i<n;i++)
    {

        for(int j = 0;j<m;j++)
        {
            if(str1[i] ==str2[j])
            {
                c[i+1][j+1] = c[i][j] + 1;
                b[i+1][j+1] = 'S';
            }
           else if(c[i+1][j]>c[i][j+1])
           {
                c[i+1][j+1] = c[i+1][j];
                b[i+1][j+1] = 'L';
           }
           else
           {
                c[i+1][j+1] = c[i][j+1];
                b[i+1][j+1] = 'U';
           }
        }
    }

}

int main()
{
    cin>>str1>>str2;
     n = str1.length();
     m = str2.length();
    solve();
    cout<<c[n][m]<<endl;
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
            cout<<c[i][j]<<' ';
        cout<<endl;
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
            cout<<b[i][j]<<' ';

        cout<<endl;
    }
    printLCS(n,m);

    return 0;
}
原文地址:https://www.cnblogs.com/plxx/p/3235440.html