最长公共子序列的长度(LCS)

  今天做到力扣的求不相交的线的数量

1035. 不相交的线 - 力扣(LeetCode)

  本质上是求两个数组的最长公共子序列的长度,之前学的算法基础已经忘了,现在来回顾一下。

  首先弄明白什么是子序列,子序列是在一个数组中,按照索引递增的顺序取部分元素组成的序列。元素不一定挨着,但是要与原数组的顺序保持一致,也就是前面说的索引递增的顺序。

  用数学公式表达:数组A={a1,a2,...,am},那么数组 A的子序列 A'={ai,aj,..ak} i<j<...<k i,j,k∈[1,m]

 最长共子序列不唯一,但是最长公共子序列的长度是唯一的

  如果我们要求数组A和数组B的最长公共子序列,数组B表示为B={b1,b2,...,bn},假设数组Z是最终的最长公共子序列Z={z1,...,zl},长度为l

  1. 如果am==bn,那么zl一定等于am/bn。(这个很好明白,A,B数组最后一个元素相等的话,最长公共子序列中必包含该元素,包含该元素不会影响到前面的元素,只是追加而已)
  2. 如果am!=bn且zl!=am,那么A数组去掉最后一个元素A{m-1}和B数组的最长公共子序列还是Z数组(也就是说am这个元素不影响求A,B的最长公共子序列)
  3. 同理,如果am!=bn且zl!=bn,那么B数组去掉最后一个元素B{n-1}和A数组的最长公共子序列还是Z数组(也就是说bn这个元素不影响求A,B的最长公共子序列)

  将上面的思路整理一下,我们要讨论的就是A数组和B数组的末尾元素是否相等,如果相等最长公共子序列必包含该元素;如果不相等,A,B的最长公共子序列长度就是

max(数组A{m-1}与数组B的最长公共子序列长度   ,   数组A和数组B{n-1} 的最长公共子序列的长度)

  因为我们不知道am等于zl还是bn等于zl,所以肯定是两个都算出来,再取最大值。

上面的讨论是基于数组的末尾元素进行讨论的,最后想求最长公共子序列的长度,我们不能把AB数组整体来看,得先让A数组有一个元素然后讨论,再追加一个元素然后讨论,同理B数组也是,这样才能求出。所以我们使用一个二维数组c[][],其中第i行表示将A数组截断到第i个元素,第j列 表示将B数组截断到第j的元素,ai,bj 作为末尾元素进行讨论,c[i][j]表示截断到第i个元素的A数组和截断到第j个元素的B数组的最长公共子序列的长度,那么有:

   可以发现,当我们判断A数组第一个元素(下标是0)和B数组第一个元素的时候c[i-1]c[j-1]都会出问题,所以c二维数组在创建的时候多创建一行和一列,这样对于c[i-1]和c[j-1]就不会有问题了,多出来的一行和一列默认值是0(java和C应该默认值是0其他的不知道),不会影响我们的比较。上面的公式就会变成:

  我们从c[1][1]开始遍历,一行一行遍历,最后c[A的数组长度][B的数组长度]就是答案。

代码:

 public int maxUncrossedLines(int[] nums1, int[] nums2) {
         int [][] c=new int[nums1.length+1][nums2.length+1];
         for(int i=1;i<=nums1.length;i++)
         {
             for(int j=1;j<=nums2.length;j++)
             {
                if(nums1[i-1]==nums2[j-1])
                {
                    c[i][j]=c[i-1][j-1]+1;
                }else{
                    c[i][j]=Math.max(c[i-1][j],c[i][j-1]);
                }
             }
         }
         return c[nums1.length][nums2.length];
    }

可以自己拿个例子试一下,填格子很快的,比较当前两个末尾元素是否相等,相等就拿对角线元素加一;不相等就看上面一个元素和左面一个元素谁更大。填的很快~ 

参考文章:

动态规划解最长公共子序列(LCS)(附详细填表过程)-CSDN博客

最长公共子序列 (LCS) 详解+例题模板(全)-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值