当前位置: 首页 > news >正文

第四十四天:动态规划part11(第九章)

1.最长公共子序列

1143. 最长公共子序列 - 力扣(LeetCode)

  1. 状态转移

    • 如果当前字符相等

      dp[i][j]=dp[i−1][j−1]+1dp[i][j] = dp[i-1][j-1] + 1dp[i][j]=dp[i−1][j−1]+1

      把它们加到前面 LCS 的基础上。

    • 如果当前字符不相等

      dp[i][j]=max⁡(dp[i−1][j], dp[i][j−1])dp[i][j] = \max(dp[i-1][j],\ dp[i][j-1])dp[i][j]=max(dp[i−1][j], dp[i][j−1])

      舍弃 text1 当前字符或 text2 当前字符,取较长结果。

  2. 初始化

    • dp[0][*] = 0dp[*][0] = 0(一个字符串为空时,公共子序列长度为 0)。

  3. 最终答案

    • dp[len(text1)][len(text2)]


3. 复杂度分析

  • 时间复杂度:O(m×n)O(m \times n)O(m×n)

  • 空间复杂度:O(m×n)O(m \times n)O(m×n),可优化到 O(min⁡(m,n))O(\min(m, n))O(min(m,n))。

class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:# 子序列不要求连续#dp[i][j]表示以text1[i-1]和text2[j-1]结尾的最长公共子序列的长度dp = [[0] * (len(text2)+1) for _ in range(len(text1)+1)]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])return dp[-1][-1]

2.不相交的线

1. 问题本质

  • 给定两个数组 nums1nums2,可以在两个数组中连接相等的数字画线,要求直线不能相交

  • 不相交 的含义是:在两个数组中连接的元素必须保持相对顺序。

  • 这和 最长公共子序列(LCS) 问题是完全等价的:

    顺序相同,不要求连续。


2. 动态规划思想

状态定义

  • dp[i][j] 表示:

    nums1[0..i-1]nums2[0..j-1] 的最大不相交直线数量。

状态转移

  1. 如果 nums1[i-1] == nums2[j-1]

    • 这两个点可以连一条线,数量 = 之前子问题的数量 + 1

      dp[i][j]=dp[i−1][j−1]+1dp[i][j] = dp[i-1][j-1] + 1dp[i][j]=dp[i−1][j−1]+1
  2. 如果不相等:

    • 只能舍弃 nums1 的当前元素或 nums2 的当前元素,取较大值

      dp[i][j]=max⁡(dp[i−1][j],dp[i][j−1])dp[i][j] = \max(dp[i-1][j], dp[i][j-1])dp[i][j]=max(dp[i−1][j],dp[i][j−1])

初始化

  • dp[0][*] = 0

  • dp[*][0] = 0

答案

  • 最终 dp[m][n](m 和 n 是两个数组的长度)即为最大不相交直线数。

class Solution:def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:#如果两条线不相交,说明连接的数字在两个数组中 顺序一致。#这与 LCS 的定义完全一致(顺序相同,不要求连续)。m = len(nums1)n = len(nums2)dp = [[0]*(n+1) for _ in range(m+1)]for i in range(1, m+1):for j in range(1, n+1):if nums1[i-1] == nums2[j-1]:dp[i][j] = dp[i-1][j-1]+1else:dp[i][j] = max(dp[i][j-1], dp[i-1][j])return dp[-1][-1]

3.最大子序和

53. 最大子数组和 - 力扣(LeetCode)

最大子数组和(Maximum Subarray)动态规划思路总结

1. 问题定义

给定整数数组 nums,找到一个连续子数组,使其元素和最大,返回该最大和。


2. 状态定义

  • dp[i] 表示以 nums[i] 结尾 的连续子数组的最大和。


3. 状态转移方程

dp[i]=max⁡(dp[i−1]+nums[i], nums[i])dp[i] = \max(dp[i-1] + nums[i],\ nums[i])dp[i]=max(dp[i−1]+nums[i], nums[i])

  • 两种选择:

    • 扩展之前的子数组(即加上当前元素)

    • 从当前元素重新开始(放弃之前的和)


4. 初始化

dp[0]=nums[0]dp[0] = nums[0]dp[0]=nums[0]


5. 结果

  • 最大子数组和为:

max⁡0≤i<ndp[i]\max_{0 \leq i < n} dp[i]0≤i<nmax​dp[i]


6. 算法复杂度

  • 时间复杂度:O(n)O(n)O(n) ,只需遍历一次数组。

  • 空间复杂度:O(n)O(n)O(n),存储状态数组,可优化为 O(1)O(1)O(1) 使用滚动变量。


7. 贪心思想

  • 在动态规划中体现为“如果之前的累计和是负数,则舍弃之前的和,从当前元素重新开始”,局部最优保证全局最优。

两种解法:

第一种动态规划:

class Solution:def maxSubArray(self, nums: List[int]) -> int:#dp[i] 表示以 nums[i] 结尾的最大连续子数组和。dp = [0] * len(nums)dp[0] = nums[0]for i in range(1, len(nums)):dp[i] = max(dp[i-1]+nums[i], nums[i])return max(dp)

第二种贪心:

class Solution:def maxSubArray(self, nums: List[int]) -> int:#最大和的连续子数组#只要连续和不是负数,一直往后加Sum = 0result = float('-inf')for i in range(len(nums)):if Sum >= 0:Sum += nums[i]result = max(Sum, result)else:Sum = nums[i]return result

4.判断子序列

392. 判断子序列 - 力扣(LeetCode)

class Solution:def isSubsequence(self, s: str, t: str) -> bool:#dp[i][j] 表示以下标i-1结尾的字符串s和以下标j-1结尾的字符串t,相同子序列的长度是dp[i][j]m = len(s)n = len(t)dp = [[0]*(n+1) for _ in range(m+1)]for i in range(1, m+1):for j in range(1, n+1):if s[i-1] == t[j-1]:dp[i][j] = dp[i-1][j-1] + 1#如果不匹配,则跳过 t[j-1] 字符,即 dp[i][j] = dp[i][j-1]else:dp[i][j] = dp[i][j-1]if dp[m][n] == m:return Truereturn False

另一种双指针写法:

class Solution:def isSubsequence(self, s: str, t: str) -> bool:i, j = 0, 0while i < len(s) and j < len(t):if s[i] == t[j]:i += 1j += 1return i == len(s)

今天结束!

http://www.dtcms.com/a/329809.html

相关文章:

  • 存量竞争下的破局之道:品牌与IP的双引擎策略|创客匠人
  • scrapy 爬虫
  • sqli-libs通关教程(51-65)
  • YOLOv11+TensorRT部署实战:从训练到超高速推理的全流程
  • 【后端】Spring框架控制反转(IoC)与依赖注入(DI)解析
  • 从零用 NumPy 实现单层 Transformer 解码器(Decoder-Only)
  • 《红黑树驱动的Map/Set实现:C++高效关联容器全解析》
  • 基于微信小程序的生态农产销售管理的设计与实现/基于C#的生态农产销售系统的设计与实现、基于asp.net的农产销售系统的设计与实现
  • Ubuntu24.04桌面版安装wps
  • 深入分析Linux文件系统核心原理架构与实现机制
  • RS485转profinet网关接M8-11 系列 RFID 读卡模块实现读取卡号输出
  • 元数据与反射:揭开程序的“自我认知”能力
  • 【递归、搜索与回溯算法】穷举、暴搜、深搜、回溯、剪枝
  • 第七章:OLED温湿度显示系统
  • 数据库连接池如何进行空闲管理
  • 光伏板横铺VS竖铺,布局决定发电量!
  • MySQL数据库知识体系总结 20250813
  • iOS混淆工具有哪些?数据安全与隐私合规下的防护实践
  • [ai]垂直agent|意图识别|槽位提取
  • 基于Tensorflow2.15的图像分类系统
  • MySQL三大存储引擎对比:InnoDB vs MyISAM vs MEMORY
  • 【Unity3D】Spine黑线(预乘问题)、贴图边缘裁剪问题
  • Effective C++ 条款39:明智而审慎地使用private继承
  • RabbitMQ:Windows版本安装部署
  • Java研学-RabbitMQ(六)
  • 基于js和html的点名应用
  • B站 韩顺平 笔记 (Day 17)
  • Spring Security 前后端分离场景下的会话并发管理
  • Spring Boot项目调用第三方接口的三种方式比较
  • 【Linux学习|黑马笔记|Day3】root用户、查看权限控制信息、chmod、chown、快捷键、软件安装、systemctl、软连接、日期与时区