leetcode动态规划(最长上升子序&正则表达式匹配)

在leetcode题库中,动态规划标签下题目数量稳居各标签下题目数量前列。想要学习算法或找工作笔试,动态规划是一个避不开的知识点。
但是,只要了解动态规划的解题步骤,大部分的动态规划题目求解并不会太困难。

0. 动态规划 简介

  • 动态规划思想:将原问题拆解成重复子问题,然后递归地找到每个子问题的最优解,最后找到全局最优解。
  • 关键点:初始状态、状态转移方程、边界条件
  • 解题三步走:
    • 判断是否适用
      • 考虑能否将问题规模减小
      • 状态间是否存在关联
    • 总结状态转移方程
      • 一维动态规划
      • 二维动态规划
      • 区间动态规划
    • 确定边界条件

话不多说,我们看看leetcode题目!

1. 一维动态规划 (300.最长上升子序列)

在这里插入图片描述

1.1 判断是否使用DP(动态规划)

当输入数组为nums=[10] 时,最长上升子序长度为1 ([10]);
当输入数组为nums=[10,15] 时,最长上升子序长度为2 ([10,15]);
当输入数组为nums=[10,15,11] 时,最长上升子序长度为2 ([10,11]);
当输入数组为nums=[10,15,11,12] 时,最长上升子序长度为3 ([10,11,12]);

  • 可以看出,原问题是可以被拆分为多个重复子问题迭代运算的,即动态规划算法适用。
1.2 状态转移方程

从上面描述中可以看出,只需判断新加入元素e与nums中每一个元素的大小关系,即可计算新的最长上升子序长度。采用dp[i]记录nums[0:i]的最长上升子序长度。例如,

  • 当nums=[10],dp[0]=1;
    nums=[10,15],dp[1]=2
    nums=[10,15,11],dp[2]=2
    当新元素12加入nums=[10,15,11]时,则分别比较12与10、15、11的大小关系,如果12>nums[j] (0≤j<≤2),则dp[3]=dp[j]+1。
    所以状态转移方程 d p [ i ] = M a x ( d p [ j ] ) + 1 , 其 中 0 ≤ j < i 且 n u m s [ j ] < n u m s [ i ] dp[i] = Max\left(dp[j]\right)+1 , 其中0≤j<i且nums[j]<nums[i] dp[i]=Max(dp[j])+1,0j<inums[j]<nums[i]
1.3 边界情况

此题的边界条件比较简单,当nums长度为1,最长上升子序长度为1,即dp[0]==1。

解题过程动画

在这里插入图片描述

代码实现:
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = [1] * len(nums)
        # 遍历nums元素,计算以当前元素结尾时的最长上升子序长度
        for i in range(len(nums)): 
            tmpmax = 1
            # 比较新加入元素与第0到第(i-1)个元素的大小,并更新最长上升子序长度
            for j in range(0 ,i):
                if nums[j] < nums[i]:
                    if tmpmax < dp[j] + 1:
                        tmpmax = dp[j] + 1
            dp[i] = tmpmax
        return max(dp)

2. 二维动态规划 (10.正则表达式匹配)

在这里插入图片描述

2.1 判断是否使用DP(动态规划)

如果,s = “abc”, p = “abc”,那么s、p是否可以匹配,则决定于s2 = "ab"和p2 = "ab"是否匹配,以及字符串s的最后一个字母与字符串s最后一个字母是否匹配。即原问题可以拆分为重复子问题

2.2 状态转移方程

假设当前子问题下,字符串s的长度为i-1,字符串p的长度为j-1。根据题目可知字符串p最后一个字符的取值有三种情况。

  • p的最后一个字符是字母,那么如果s[i]==p[j],且s[0:i-1]与p[0:j-1]匹配,则s与p匹配。
  • p的最后一个字符是".",那么只要s[0:i-1]与p[0:j-1]匹配,则s与p匹配。
  • p的最后一个字符是"* ",那么表示p的第j−1个字符匹配任意自然数次(包括0次);
    • 如果匹配0次,只要s[0:i]与p[0:j-2]匹配,则s与p匹配;
    • 如果匹配非0次,需要s[i]==p[j-1] (p[j-1]为"."),且s[0:i-1]与p[0:j]匹配,才满足s与p匹配。

使用二维数组dp[i][j]表示s的前i个字符与p中的前j个字符是否能够匹配,取值布尔类型。则状态转移方程:

2.3 边界情况
  • 若字符串s长度为i,p长度为j,数组dp长度(i+1)*(j+1),dp[0][0]
    =True,表示两个空字符串匹配。
  • dp[0][k]表示字符串s为空,p不为空情况,需要提前对该行进行初始化。

在这里插入图片描述

代码实现:
class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        len_p = len(p)
        len_s = len(s)
        matrix = [[False for i in range(len_s+1)] for j in range(len_p+1)]
        matrix[0][0] = True
        k = 0
        for i in range(len_p):
            if p[i]=='*':
                k = k+1
                if k*2 > i:
                    matrix[i+1][0] = True
        for i in range(1, len_p+1):
            for j in range(1, len_s+1):
                if p[i-1] == s[j-1] or p[i-1]==".":
                    matrix[i][j] = matrix[i-1][j-1]
                elif p[i-1] == "*":
                    matrix[i][j] = matrix[i - 2][j]
                    if p[i-2] == s[j-1] or p[i-2]=='.':
                        matrix[i][j] |= matrix[i][j-1]
                else:
                    matrix[i][j] = False
        return matrix[len_p][len_s]

更多内容欢迎关注个人公众号:PythonAndDataTech
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值