最长公共(上升)子序列

1、最长公共子序列(LCS)

题目: Common Subsequence
题目描述: 给定两个字符串A和B(或数字序列),求一个字符串,使得这个字符串是所给两个字符串的最长公共部分(可以不连续)
动态规划的做法:时间复杂度,O(n*m)
设dp[i][j]表示A的i号位和B的j号位前的LCS,我们从下标1以开始存字符串数组(如果从0开始存还要一个一个讨论边界是0还是1),初始化dp[0][0]=0,比如说A=wesd,B=wzeji,dp[2][3]=2(就是we和wze的LCS),然后我们来看dp[i][j]的转移方程,分A[i]=b[j]和a[i]!=b[j]两种情况
在这里插入图片描述来源:《算法笔记》,胡凡,曾磊主编

代码:

#include <iostream>
#include <string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int manx=2e3+10;
const int INF=1e9;
int dp[manx][manx];
int main()
{
    char str1[manx],str2[manx];
    while(scanf("%s%s",str1+1,str2+1)!=EOF)
    {
        int i,j;
        int l1=strlen(str1+1);
        int l2=strlen(str2+1);
        memset(dp,0,sizeof(dp));
        for(i=1; i<=l1; i++)
            for(j=1; j<=l2; j++)
            {
                if(str1[i]==str2[j])
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        printf("%d\n",dp[i-1][j-1]);
    }
}

2、最长公共上升子序列(LCIS)

例题:Greatest Common Increasing Subsequence
题目描述: 在最长公共子序列的基础上,增加一个上升子序列的条件

假设和最长公共子序列一样,我们规定dp[i][j]表示A的i号位和B的j号位前的LCIS。在最长公共子序列中,当A[i]=B[j]时,我们直接用dp[i-1][j-1]+1来更新dp[i][j]。但在这里,因为有上升的限制,就不能直接通过dp[i-1][j-1]+1来得到dp[i][j]了,因为A的i-1号位和B的j-1号位前的LCIS中的最后一位数不一定 < < <A[i]。

所以为了判断能否通过前面的dp值来更新dp[i][j],我们这样规定:设dp[i][j]表示A的i号位和B的j号位前并且以B[j]为结尾的LCIS的长度。

这样当A[i]=B[j]时,我们就可以寻找最大的dp[i-1][k]来更新dp[i][j](1 ≤ \leq k ≤ \leq j-1,B[k] < < <B[j])

最后就剩下了怎么确定k值的问题,很容易想到我们可以枚举1~j-1中所有符合条件(B[k] < < <B[j])的dp[i-1][k]值,但这样复杂度就到了O(n^3)。所以这里我们换一个思路:在第二层循环中,我们每次从左至右都更新了dp[i][1]到dp[i][m]所有值(m是序列B的长度),即以序列A固定的前i位去匹配序列B,所以这里A[i]值是固定的,我们就可以维护一个pos值,使得dp[i-1][pos]是在条件B[pos]<A[i]下的最优解。当A[i]=B[j]时,我们就可以直接用dp[i-1][pos]+1来更新dp[i][j]。

代码实现:

ans=0;
memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
{
    int pos=0;
    for(int j=1; j<=m; j++)
    {
          if(a[i]>b[j]&&dp[i-1][pos]<dp[i-1][j])
         	  pos=j;//以b[j]结尾,a[1]~a[i]可以匹配的最大长度
          if(a[i]==b[j])
          {
              dp[i][j]=dp[i-1][pos]+1;
              ans=max(ans,dp[i][j]);
          }
      	 else dp[i][j]=dp[i-1][j];
    }
}

看到这里不难发现每次更新只用到了dp[i-1][]的那一层,所以dp我们可以优化成一维。

ans=0;
memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
{
    int pos=0;
    for(int j=1; j<=m; j++)
    {
          if(a[i]>b[j]&&dp[pos]<dp[j])
          pos=j;
          if(a[i]==b[j])
          {
              dp[j]=dp[pos]+1;
              ans=max(ans,dp[j]);
          }
    }
}

参考博客:zoj2432 hdoj1423 最长公共上升子序列(LCIS)
变型例题:吉哥系列故事——完美队形I

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值