LIS与LCS是动态规划中最常见的两种情况,LIS也就是最长上升子序列,而LCS是最长公共子序列。
在解决这个问题之前,先要明白为什么是序列,举个例子来说明,在数组 [1,2,3,4,5,6]中,[2,3,5]就是其子序列,也就是说,子序列其实就是数组中存在先后顺序,但不强调连续的子数组。那么,在了解了序列是什么之后,就来看一下题吧。
首先是LIS:即求最长上升子序列。
那么既然是动态规划,那就要去找状态转移方程,那么,我可以设置为对于dp【j】表示以nums【j】为结尾的递增子序列的长度
那么这时,就可以尝试用代码来实现:
这里就是用dp数组表示当前的最大上升子序列的长度的代码,那么对于这个来处理的方法就是如下,首先遍历nums数组,然后再遍历nums【i】之前的所有数字nums【j】若是当前的数字nums【i】的值比nums【j】大,那么就二者进行比较,此时运用max数组来对dp【i】和dp【j】进行比较,来决定增加,最后输出dp数组中的最大值,最后,就可以得到答案。
但是,这个代码只是O(n2)的复杂度,只可以处理104级别的数据,因此,需要去优化代码,
那么如何去优化呢,可以尝试如下的方案,将其优化到O(nlogn)的级别,那么如何来处理?
便是直接去遍历数组,维护一个单调递增的序列即可。
Python
class Solution:def lengthOfLIS(self, nums):from bisect import bisect_leftn=len(nums)dp=[]for i in range(n):if not dp or nums[i]>dp[-1]:dp.append(nums[i])else:idx=bisect_left(dp,nums[i])dp[idx]=nums[i]return len(dp)
在这里其实就是在维护一个单带增加的序列,因为从第一个数开始,由于求递增序列,在遍历的过程中,若是当前的数比dp的最后一项大,那么这个数一定会让序列增加,而若是这个数字小于等于dp数组的最后一个数,那么这个数一定不会让递增子序列的数字增加,这时,就使用二分查找来更新dp数组,将这个数字替换到对应的位置,这样子就可以维护一个dp数组,最后dp数组的长度就是最大dp递增子序列。
这个就是一个更优的题解。
而LCS则是去求最长公共子序列
要求解这道题,就要去找状态转移方程,这里由于有两个字符串,那么就要使用二维dp,dp【i】【j】表示分别到i为止和到j为止最长公共子序列的长度,那么接着就是去找状态转移方程。这道题的状态转移方程就是
dp【i】【j】=dp【i-1】【j-1】 --------当str1【i】==str2【j】的时候;
dp【i】【j】=max(dp【i-1】【j】,dp【i】【j-1】)--------其他情况。
那么在这里就可以写出代码了
class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:dp=[[0]*(len(text2)+1) for i in range(len(text1)+1)]# for i in dp:# print(i)for i in range(1,len(text1)+1):for j in range(1,len(text2)+1):if text1[i-1]==text2[j-1]:dp[i][j]=dp[i-1][j-1]+1else:dp[i][j]=max(dp[i-1][j],dp[i][j-1])# for i in dp:# print(i)return dp[len(text1)][len(text2)]
这里就是关于这道题的实现。
那就到这里了。