动态规划(Dynamic Programming,简称DP)是一种在算法设计中非常有用的技术,它通过将复杂问题分解为更小的子问题,并将子问题的解存储起来,避免了重复计算,从而提高了算法的效率。上周的题目主要是动态规划的基础,这周刷的题包括最长回文子串、最长上升子序列、兑换零钱和编辑距离等较为困难的部分。
1. 最长回文子串
回文是正读和反读都相同的字符串。最长回文子串问题是找出给定字符串中最长的回文子串。
思路:
- 我们可以使用动态规划来解决这个问题。设
dp[i][j]表示从索引i到j的子串是否是回文。 - 如果
s[i] == s[j],那么dp[i][j]的值取决于dp[i + 1][j - 1]。 - 如果
s[i] != s[j],则dp[i][j]为False。
算法步骤:
- 初始化
dp数组,所有值为False。 - 将所有长度为1的子串设置为回文(
dp[i][i] = True)。 - 考虑长度为2的子串,如果两个字符相同,则设置为回文。
- 对于更长的子串,使用递推关系更新
dp数组。 - 遍历
dp数组,找到最大的True区域,即为最长回文子串。
2. 最长上升子序列
最长上升子序列问题是找出给定整数序列中最长的严格递增子序列。
思路:
- 动态规划数组
dp[i]表示以第i个元素结尾的最长上升子序列的长度。 - 状态转移方程为:
dp[i] = max(dp[i], dp[j] + 1),其中j < i且arr[j] < arr[i]。
算法步骤:
- 初始化
dp数组,所有值为1。 - 遍历数组,对于每个元素,检查其之前的所有元素,更新
dp数组。 - 最大值即为最长上升子序列的长度。
3. 兑换零钱
兑换零钱问题是给定一定数量的硬币和总金额,找出最少需要多少个硬币。
思路:
- 使用动态规划数组
dp[i]表示组成金额i所需的最少硬币数量。 - 状态转移方程为:
dp[i] = min(dp[i - coin] + 1, dp[i])。
算法步骤:
- 初始化
dp数组,dp[0] = 0,其他值为无穷大。 - 遍历每个硬币,更新
dp数组。 dp[amount]即为所需的最少硬币数量。
4. 编辑距离
编辑距离问题是计算两个字符串之间,通过插入、删除或替换字符,将一个字符串转换为另一个字符串所需的最少操作数。
思路:
- 使用动态规划数组
dp[i][j]表示将第一个字符串的前i个字符转换为第二个字符串的前j个字符所需的最少操作数。 - 状态转移方程取决于两个字符串的当前字符是否相同。
算法步骤:
- 初始化
dp数组,dp[0][j] = j,dp[i][0] = i。 - 遍历两个字符串,根据当前字符是否相同更新
dp数组。 dp[m][n]即为所需的最少操作数。
结语
通过本周对动态规划的深入学习,进一步掌握了解决上述问题的方法,还学会了如何将动态规划应用于其他问题。与其说难度提高,不如说是更加需要将具象的题目抽象化的能力,得出状态转移方程,进一步将方程翻译为代码,问题便可迎刃而解。