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

初学python的我开始Leetcode题-16

提示:100道LeetCode热题-16主要是多维动态规划相关,包括五题:不同路径、最小路径和、最长回文子串、最长公共子序列、编辑距离。由于初学,所以我的代码部分仅供参考。


前言

之前做过动态规划,这里扩展到多维情况~


提示:以下是本篇文章正文内容,下面结果代码仅供参考

题目1:不同路径

1.题目要求:

题目如下:

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 10^{9}

代码框架已经提供如下:

class Solution(object):

    def uniquePaths(self, m, n):

        """

        :type m: int

        :type n: int

        :rtype: int

        """

2.结果代码:

class Solution(object):def uniquePaths(self, m, n):""":type m: int:type n: int:rtype: int"""dp = [[1] * n for _ in range(m)]for i in range(1, m):for j in range(1, n):dp[i][j] = dp[i-1][j] + dp[i][j-1]return dp[-1][-1]

说明:

首先,我需要明确题目在问什么。我们有一个m行n列的网格,机器人从左上角(Start)出发,每次只能向右或向下移动一步,目标是到达右下角(Finish)。我们需要计算所有可能的不同路径的数量。机器人需要从左上角(0,0)移动到右下角(m-1,n-1)。在这个过程中:

  • 需要向右移动 (n-1) 次。

  • 需要向下移动 (m-1) 次。

  • 总共的移动步数为 (m-1 + n-1) = m + n - 2 步。

因此,问题转化为:在 m + n - 2 步中,选择 (m-1) 步向下移动(或 (n-1) 步向右移动)的组合数,因此可以使用组合数的方法。虽然组合数学的方法很高效,但本单元是动态规划(DP)的方法,这在某些变种问题(如网格中有障碍物)中可能更灵活:

定义 dp[i][j] 为从 (0,0) 到 (i,j) 的路径数。

递推关系:

  • dp[i][j] = dp[i-1][j] + dp[i][j-1] (因为可以从上方或左方到达)

  • 边界条件:

    • dp[0][j] = 1 (第一行只能一直向右)

    • dp[i][0] = 1 (第一列只能一直向下)

题目2:最小路径和

1.题目要求:

题目如下:

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例 1:

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 200

代码框架已经提供如下:

class Solution(object):

    def minPathSum(self, grid):

        """

        :type grid: List[List[int]]

        :rtype: int

        """

2.结果代码:

class Solution(object):def minPathSum(self, grid):""":type grid: List[List[int]]:rtype: int"""if not grid or not grid[0]:return 0m, n = len(grid), len(grid[0])dp = [0] * ndp[0] = grid[0][0]for j in range(1, n):dp[j] = dp[j-1] + grid[0][j]for i in range(1, m):dp[0] += grid[i][0]for j in range(1, n):dp[j] = min(dp[j], dp[j-1]) + grid[i][j]return dp[-1]

说明:

这是一个典型的动态规划问题。我们可以定义 dp[i][j] 为从 (0, 0)(i, j) 的最小路径和。

递推关系:

  • dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])

    • 可以从上方 (i-1, j) 或左方 (i, j-1) 到达 (i, j),选择其中较小的路径和加上当前格子的值。

边界条件:

  • dp[0][0] = grid[0][0](起点)

  • 第一行 dp[0][j] 只能从左方到达:dp[0][j] = dp[0][j-1] + grid[0][j]

  • 第一列 dp[i][0] 只能从上方到达:dp[i][0] = dp[i-1][0] + grid[i][0]

题目3:最长回文子串

1.题目要求:

题目如下:

给你一个字符串 s,找到 s 中最长的 回文 子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

代码框架已经提供如下:

class Solution(object):

    def longestPalindrome(self, s):

        """

        :type s: str

        :rtype: str

        """

2.结果代码:

class Solution(object):def longestPalindrome(self, s):""":type s: str:rtype: str"""if not s:return ""start, end = 0, 0for i in range(len(s)):# 奇数长度left1, right1 = self.expand_around_center(s, i, i)# 偶数长度left2, right2 = self.expand_around_center(s, i, i + 1)if right1 - left1 > end - start:start, end = left1, right1if right2 - left2 > end - start:start, end = left2, right2return s[start:end + 1]def expand_around_center(self, s, left, right):while left >= 0 and right < len(s) and s[left] == s[right]:left -= 1right += 1return left + 1, right - 1

以上是中心扩展法:

回文可以有两种形式:

  • 奇数长度:中心是一个字符,如 "aba"。

  • 偶数长度:中心是两个字符,如 "abba"。

因此,我们需要对每种可能的中心进行扩展:

  • 对于每个字符 s[i],以 s[i] 为中心扩展。

  • 对于每对字符 s[i]s[i+1],以 s[i]s[i+1] 为中心扩展。

实现步骤:

  1. 初始化最长回文子串的起始和结束索引。

  2. 遍历每个字符作为中心:

    • 扩展奇数长度的回文。

    • 扩展偶数长度的回文。

  3. 更新最长回文子串的索引。

  4. 返回对应的子串。

如果要动态规划的话,空间复杂度O(n^{2}):

class Solution(object):def longestPalindrome(self, s):""":type s: str:rtype: str"""if not s:return ""n = len(s)# dp[i][j] 表示 s[i..j] 是否为回文dp = [[False] * n for _ in range(n)]start, max_len = 0, 1  # 至少有一个字符# 长度 1for i in range(n):dp[i][i] = True# 长度 2for i in range(n - 1):if s[i] == s[i + 1]:dp[i][i + 1] = Truestart = imax_len = 2# 长度 >= 3for length in range(3, n + 1):          # 当前考察的子串长度for i in range(n - length + 1):j = i + length - 1if s[i] == s[j] and dp[i + 1][j - 1]:dp[i][j] = Truestart = imax_len = lengthreturn s[start: start + max_len]

1. 思路与状态定义

dp[i][j] 表示子串 s[i … j] 是否为回文,其中 0 ≤ i ≤ j < n
则:

  • dp[i][j] = True 当且仅当
    s[i] == s[j]
    如果 j - i + 1 > 2,还需满足 dp[i+1][j-1] == True

2. 递推公式

if s[i] == s[j]:if j - i <= 2:               # 长度 1 或 2dp[i][j] = Trueelse:dp[i][j] = dp[i+1][j-1]
else:dp[i][j] = False

3. 填表顺序

  • 按长度递增(从 1 到 n)
    先填所有长度为 1、2、3 … n 的子串,确保 i+1j-1 已经计算过。

4. 边界初始化

  • 所有长度为 1 的子串都是回文:dp[i][i] = True

  • 记录当前最长回文子串的起始索引及长度即可。

题目4:最长公共子序列

1.题目要求:

题目如下:

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1 和 text2 仅由小写英文字符组成。

代码框架已经提供如下:

class Solution(object):

    def longestCommonSubsequence(self, text1, text2):

        """

        :type text1: str

        :type text2: str

        :rtype: int

        """

2.结果代码:

class Solution(object):def longestCommonSubsequence(self, text1, text2):m, n = len(text1), len(text2)# dp[i][j] 表示 text1[:i] 与 text2[:j] 的 LCS 长度dp = [[0] * (n + 1) for _ in range(m + 1)]for i in range(1, m + 1):for j in range(1, n + 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[m][n]

这是个经典 二维 DP 问题。
dp[i][j] 表示 text1[0 .. i-1]text2[0 .. j-1] 的最长公共子序列(LCS)长度。
状态转移:

  • 如果 text1[i-1] == text2[j-1],则这个字符一定在 LCS 中
    dp[i][j] = dp[i-1][j-1] + 1

  • 否则,取“跳过 text1 最后一个字符”或“跳过 text2 最后一个字符”的最大值
    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

边界:dp[0][*] = dp[*][0] = 0(空串的 LCS 长度为 0)

时间复杂度:O(m * n)
空间复杂度:O(m * n),可压缩为 O(min(m, n))

题目5:编辑距离

1.题目要求:

题目如下:

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

提示:

  • 0 <= word1.length, word2.length <= 500
  • word1 和 word2 由小写英文字母组成

代码框架已经提供如下:

class Solution(object):

    def minDistance(self, word1, word2):

        """

        :type word1: str

        :type word2: str

        :rtype: int

        """

2.结果代码:

class Solution(object):def minDistance(self, word1, word2):m, n = len(word1), len(word2)# dp[i][j]: word1[:i] -> word2[:j] 的最小操作数dp = [[0] * (n + 1) for _ in range(m + 1)]# 边界for i in range(m + 1):dp[i][0] = ifor j in range(n + 1):dp[0][j] = jfor i in range(1, m + 1):for j in range(1, n + 1):if word1[i - 1] == word2[j - 1]:dp[i][j] = dp[i - 1][j - 1]else:dp[i][j] = min(dp[i - 1][j],     # 删除dp[i][j - 1],     # 插入dp[i - 1][j - 1]  # 替换) + 1return dp[m][n]

说明:

这是经典的 编辑距离 / Levenshtein 距离 问题:
把字符串 word1 转换成 word2 最少需要多少次插入、删除或替换操作。

1. 状态定义

dp[i][j] 表示 word1[0..i-1] 转换成 word2[0..j-1] 所需的最少操作数。

2. 状态转移

考虑 word1[i-1]word2[j-1] 的关系:

  • 相等:不需要操作,dp[i][j] = dp[i-1][j-1]

  • 不等:取三种可能操作的最小值再 +1

    • 插入:dp[i][j-1] + 1

    • 删除:dp[i-1][j] + 1

    • 替换:dp[i-1][j-1] + 1

3. 边界

  • dp[0][j] = j:把空串变成 word2[0..j-1] 需要插入 j 次

  • dp[i][0] = i:把 word1[0..i-1] 变成空串需要删除 i 次


总结

针对多维动态规划的五种题型进行了学习,了解了部分有关多维动态规划与python的相关知识,大家加油!

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

相关文章:

  • CTFshow系列——命令执行web49-52
  • assert使用方法
  • Redis 高可用开发指南
  • 力扣594:最和谐子序列
  • 客流特征识别误报率↓76%!陌讯多模态时序融合算法在智慧零售的实战解析
  • Tesla智能座舱域控制器(MCU)的系统化梳理
  • 【网络运维】Shell 脚本编程:if 条件语句
  • 【40页PPT】数字工厂一体化运营管控平台解决方案(附下载方式)
  • Spark04-MLib library01-机器学习的介绍
  • SNMP 协议的总结
  • 每日算法题【链表】:相交链表、环形链表、环形链表II
  • 鸿蒙分布式计算实战:用 ArkTS+Worker 池落地可运行任务管理 Demo,从单设备到跨设备全方案
  • [二维前缀和]1277. 统计全为 1 的正方形子矩阵
  • HarmonyOS实战(DevEco AI篇)—深度体验DevEco CodeGenie智能编程助手
  • Function + 枚举 + Map:轻量路由器的最佳实践
  • ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘192.168.24.96‘ (10060)
  • 基于Java、GeoTools与PostGIS的对跖点求解研究
  • 大数据毕业设计选题推荐:基于Spark+Django的学生创业数据分析可视化系统详解 毕业设计/选题推荐/深度学习/数据分析/数据挖掘/机器学习/随机森林
  • 网络编程socket-Udp
  • Linux网络启程
  • Java基础(十四)分布式
  • 《Distilling the Knowledge in a Neural Network》论文PDF分享, 2015 年,谷歌提出了 “知识蒸馏” 的概念
  • 深入解析Apache Kafka的核心概念:构建高吞吐分布式流处理平台
  • 07-分布式能力与多设备协同
  • Lucene 与 Elasticsearch:从底层引擎到分布式搜索平台的演进
  • Flink提交作业
  • (Redis)内存淘汰策略
  • Elastic APM vs Apache SkyWalking vs Pinpoint:APM性能监控方案对比分析与最佳实践
  • 深度学习之第二课PyTorch与CUDA的安装
  • 华为云Stack环境中计算资源,存储资源,网络资源发放前的准备工作(上篇)