最长公共子序列

题目描述

给定两个字符串 s 1 s 2 … s n s_1 s_2…s_n s1s2sn t 1 t 2 … t m t_1 t_2…t_m t1t2tm。求这两个字符串的最长的公共子序列。字符串 s 1 s 2 … s n s_1 s_2…s_n s1s2sn的子序列指可以表示为 s i 1 s i 2 … s i m ( i 1 &lt; i 2 &lt; ⋯ &lt; i m ) s_{i_1} s_{i_2}…s_{i_m} (i_1&lt;i_2&lt;⋯&lt;i_m) si1si2sim(i1<i2<<im)的序列。

解析

  • 这个问题就是著名的最长公共子序列(LCS)问题,使用下面递推DP进行求解:
    • dp数组的含义: d p [ i ] [ j ] = s 1 s 2 … s i dp[i][j]= s_1 s_2…s_i dp[i][j]=s1s2si t 1 t 2 … t j t_1 t_2…t_j t1t2tj的最长公共子序列的长度。
    • 现在我们计算 d p [ i + 1 ] [ j + 1 ] dp[i+1][j+1] dp[i+1][j+1],即 s 1 s 2 … s i + 1 s_1 s_2…s_{i+1} s1s2si+1 t 1 t 2 … t j + 1 t_1 t_2…t_{j+1} t1t2tj+1的最长公共子序列的长度,有两种可能:
      • 第一种: s ( i + 1 ) = t ( j + 1 ) s_(i+1)=t_(j+1) s(i+1)=t(j+1):那么dp[i+1][j+1]= dp[i][j]+1
      • 第二种: s i + 1 ! = t j + 1 s_{i+1} != t_{j+1} si+1!=tj+1:那么dp[i+1][j+1]应该是dp[i+1][j]或者dp[i][j+1]的最大者。
    • 公式总结如下: d p [ i + 1 ] [ j + 1 ] = { d p [ i ] [ j ] + 1 , s ( i + 1 ) = t ( j + 1 ) m a x ⁡ ( d p [ i + 1 ] [ j ] , d p [ i ] [ j + 1 ] ) , s ( i + 1 ) ! = t ( j + 1 ) ) dp[i+1][j+1]=\begin{cases} dp[i][j]+1,&amp; s_(i+1)=t_(j+1) \\ max⁡(dp[i+1][j],dp[i][j+1]) ,&amp; s_(i+1)!=t_(j+1) ) \end{cases} dp[i+1][j+1]={dp[i][j]+1,max(dp[i+1][j],dp[i][j+1]),s(i+1)=t(j+1)s(i+1)!=t(j+1))
    • 由上面的公式可知,只有dp[i+1][j]和dp[i][j+1]已经计算出来就可以求出dp[i+1][j+1],即只要当前单元的上面一个单元和左边一个单元已知就可以求出当前的单元。
    • 递推的初始条件: d p [ 0 − n ] [ 0 ] = d p [ 0 ] [ 0 − m ] = 0 dp[0-n][0]=dp[0][0-m]=0 dp[0n][0]=dp[0][0m]=0
    • 组后我们的结果在dp[n][m]里面。我们只要将dp数组中的每个元素都填满就可以计算出dp[n][m],故时间复杂度为 O ( n m ) O(nm) O(nm)

代码:

#include <iostream>

using namespace std;
#define Max_n 1005
#define Max_m 1005

int n,m;
char s[Max_n],t[Max_m];
int dp[Max_n][Max_m];
//初始化
void initialize(int n,int m){
    for(int i=0;i<=n;i++)
        dp[i][0]=0;
    for(int j=0;j<=m;j++)
        dp[0][j]=0;
}
//递推求整个dp数组
void solve(){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(s[i]==t[j]){
                dp[i][j]=dp[i-1][j-1]+1;
            }
            else{
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值