最长公共子序列(LCS)

题目链接
https://www.acwing.com/problem/content/899/

思路
      闫氏dp分析法,先上图(感觉放y总照片不太好,就划掉了)后面a表示第一个序列,b表示第二个序列,可能会穿插使用。
在这里插入图片描述
      状态表示图中已经说得很清楚了,这里状态划分以是否取a序列中的第 i i i 个数和b序列中的第 j j j 个数作为划分依据,很明显能划分成四种情况,图中椭圆的0,1代表的意思我解释一下。
      00:a序列中第 i i i 个数不取,b序列中第 j j j 个数不取的选法所表示的集合。那么这个状态转移很明显是 f [ i − 1 ] [ j − 1 ] f [ i - 1] [ j - 1 ] f[i1][j1]
      11:a序列中取第 i i i 个数,b序列中取第 j j j 个数的选法所表示的集合。这种情况很明显只有当 a [ i ] = = b [ j ] a [ i ] == b [ j ] a[i]==b[j]的时候才会取到,那这个状态转移很明显是 f [ i − 1 ] [ j − 1 ] + 1 f [ i - 1 ] [ j - 1 ]+1 f[i1][j1]+1
      01:a序列中不取第 i i i 个数,b序列中取第 j j j 个数的选法所表示的集合。
      10:a序列中取第 i i i 个数,b序列中不取第 j j j 个数所表示的集合。
      后面两种情况我没写状态转移,根据前面00和11的写法,很自然的觉得01的状态转移是 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j],10的状态转移是 f [ i ] [ j − 1 ] f [ i ] [ j - 1 ] f[i][j1]。这里就错了。
      让我们回顾一下上面状态表示的定义, f [ i ] [ j ] f [ i ] [ j ] f[i][j]所表示的是所有在第一个序列的前 i 个字母中出现,且在第二个序列的前 j 个字母中出现的子序列的集合中的长度最大值。那么将 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j] f [ i ] [ j − 1 ] f [ i ] [ j - 1] f[i][j1]代入这个定义你会发现,这个和我们状态划分的依据并不是等价的,区别在哪?区别在这两个方式表达出来的集合,例如 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j] 它所表示出来的最长公共子序列,对于这里面的的第二个序列来说不一定以 b [ j ] b [ j ] b[j] 结尾,而我们状态划分的依据这里的01是一定取 b b b序列中第 j j j 个数的,那么它一定以 b [ j ] b [ j ] b[j]结尾,所以这其实并不是等价变形。
      但是,我们再进一步分析,虽然不等价,但是我们的目的是要求解最大值而已,对于求解最大值or最小值来说,我的状态有重复其实是不影响的,求数量才会对状态划分有严格的不重叠要求,最大值or最小值无所谓,我们经过思考发现,虽然 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j]和01不等价,但是01所有的情况都包含在 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j]中,我们完全可以在这里用 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j]来求解01对应的这一类,同理 f [ i ] [ j − 1 ] f [ i ] [ j - 1] f[i][j1]也一样,而且这样表示之后,00就不需要单独列出来了,因为比如 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j]里面第二个序列不以第 j 个数结尾,那么就相当于都不取了,所以 f [ i − 1 ] [ j ] f [ i - 1 ] [ j ] f[i1][j] f [ i ] [ j − 1 ] f [ i ] [ j - 1] f[i][j1]这两个表示一起把00 01 10三种情况包含完了,所以在代码里你看不到00单独列出来的情况。下面上代码。

代码

#include<bits/stdc++.h>
using namespace std;

char a[1005];
char b[1005];
int f[1005][1005];
int n,m;

int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s%s",a+1,b+1);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            f[i][j]=max(f[i-1][j],f[i][j-1]);
            if(a[i]==b[j])
            {
                f[i][j]=max(f[i][j],f[i-1][j-1]+1);
            }
        }
    }
    printf("%d\n",f[n][m]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值