NOJ1041——算法实验四——最长公共子序列

最长公共子序列问题

问题描述

描述:

一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X=<x1, x2,…, xm>,则另一序列Z=<z1, z2,…, zk>是X的子序列是指存在一个严格递增的下标序列 <i1, i2,…, ik>,使得对于所有j=1,2,…,k有:

Xij = Zj

如果一个序列S即是A的子序列又是B的子序列,则称S是A、B的公共子序列。
求A、B所有公共子序列中最长的序列的长度。

输入:

输入共两行,每行一个由字母和数字组成的字符串,代表序列A、B。A、B的长度不超过200个字符。

输出:

一个整数,表示最长各个子序列的长度。
格式:printf("%d\n");

输入样例:

programming
contest

输出样例:

2

问题分析

设序列
X = { x 1 , x 2 , ⋯   , x m } X=\{x_1,x_2,\cdots,x_m\} X={x1,x2,,xm}

Y = { y 1 , y 2 , ⋯   , y n } Y=\{y_1,y_2,\cdots,y_n\} Y={y1,y2,,yn}

Z = { z 1 , z 2 , ⋯   , z k } Z=\{z_1,z_2,\cdots,z_k\} Z={z1,z2,,zk}

  1. x m = y n x_m = y_n xm=yn Z k − 1 Z_{k-1} Zk1 X m − 1 X_{m-1} Xm1 Y n − 1 Y_{n-1} Yn1的最长公共子序列;

  2. x m ≠ y n x_m\neq y_n xm=yn,且 x m ≠ z k x_m\neq z_k xm=zk,则 Z Z Z X m − 1 X_{m-1} Xm1 Y Y Y的最长公共子序列。

  3. y n ≠ x m y_n\neq x_m yn=xm,且 y n ≠ z k y_n\neq z_k yn=zk,则 Z Z Z X X X Y n − 1 Y_{n-1} Yn1的最长公共子序列。

分析结论

  • x m = y n x_m = y_n xm=yn时,有一个子问题,即:找出 X m − 1 X_{m - 1} Xm1 Y n − 1 Y_{n -1} Yn1的最长公共子序列。

  • x m ≠ y n x_m \neq y_n xm=yn,有两个子问题

    • (1)找出 X m − 1 X_{m-1} Xm1 Y Y Y的最长公共子序列,
    • (2)找出 X X X Y n − 1 Y_{n-1} Yn1的最长公共子序列。
  • 这些子问题都包含了同一个子问题( X m − 1 , Y n − 1 X_{m-1},Y_{n-1} Xm1,Yn1

动态规划的三个条件:

  1. 最优性原理:问题( X , Y X,Y X,Y)包含的子问题( X − 1 , Y − 1 X-1,Y-1 X1,Y1)也是最优的,复合最优性原理。
  2. 无后效性:从前到后遍历字符串 X X X Y Y Y时,后面字符串不影响前面的最长公共序列。
  3. 有重叠子问题:问题( X , Y X,Y X,Y)的子问题包含了同一个子问题( X m − 1 , Y n − 1 X_{m-1},Y_{n-1} Xm1,Yn1

所以最长公共子序列可以用动态规划法求解,

状态转移方程为:
c [ i ] [ j ] = { 0 ,   i = 0 , j = 0 c [ i − 1 ] [ j − 1 ] + 1 , x i = y i m a x { c [ i ] [ j − 1 ] , c [ i − 1 ] [ j ] } , x i ≠ y j c[i][j]= \begin{cases} 0,\ i=0,j=0 \\ c[i-1][j-1]+1,x_i=y_i\\ max\{c[i][j-1],c[i-1][j]\},x_i \neq y_j \end{cases} c[i][j]=0, i=0,j=0c[i1][j1]+1,xi=yimax{c[i][j1],c[i1][j]}xi=yj

算法流程

开始
遍历a与b字符串
a[i-1] == b[j-1]
dp[i][j] = dp[i - 1][j - 1] + 1
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

源代码

#include <iostream>
#include <string>
#define MAX 100
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int m, n;
string a, b;
int dp[MAX][MAX];
void LCSlength()
{
    int i, j;
    for (i = 1; i <= m;i++)
    {
        for (j = 1; j <= n;j++)
        {
            if(a[i-1] == b[j-1])
            {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }
            else{
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
}
int main()
{
    cin >> a >> b;
    m = a.length();
    n = b.length();
    LCSlength();
    cout << dp[m][n] << endl;
}

算法复杂度分析

代码中使用了两层循环而且长度分别为m和n,所以算法复杂度为 O ( m n ) O(mn) O(mn).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alfred young

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值