新的一年,D个P吧

156 阅读5分钟

线性DP

一、动态规划基础概念

动态规划是一种用于解决优化问题的算法策略。它的核心思想是将一个复杂的问题分解为一系列相互关联的子问题,通过求解子问题,并记录子问题的解,避免重复计算,最终得到原问题的最优解。其主要利用了问题的最优子结构性质和重叠子问题性质。

  • 最优子结构性质:一个问题的最优解包含了子问题的最优解。例如,在计算斐波那契数列时,第 n 个斐波那契数可以通过前两个斐波那契数(子问题)的最优解(它们本身的值)相加得到。
  • 重叠子问题性质:在解决问题过程中,会反复求解相同的子问题。以斐波那契数列为例,在计算第 5 个斐波那契数时,会多次计算第 3 个和第 4 个斐波那契数,这些子问题的计算是重复的。

二、线性 DP 的特点

  1. 阶段划分呈线性结构

    • 线性 DP 问题通常可以将求解过程按照一定的顺序划分为多个阶段,这些阶段是线性排列的。比如,在最长上升子序列问题中,我们可以按照序列的索引顺序,从第一个元素开始,依次考虑每个元素,这就形成了一个线性的阶段划分。
    • 假设我们有一个序列 a = [1,3,2,4],在求最长上升子序列时,我们可以先考虑只有第一个元素 1 的情况(第一阶段),然后加入第二个元素 3 看是否能构成更长的上升子序列(第二阶段),以此类推。
  2. 状态转移依赖线性关系

    • 状态通常是基于前面的一个或几个状态进行转移的。以背包问题为例(虽然它是一种特殊的线性 DP),在 0 - 1 背包问题中,设 dp [i][j] 表示前 i 个物品放入容量为 j 的背包中所能获得的最大价值。
    • 状态转移方程 dp [i][j] = max (dp [i - 1][j], dp [i - 1][j - w [i]] + v [i])(其中 w [i] 是第 i 个物品的重量,v [i] 是第 i 个物品的价值)体现了这种线性依赖关系。当前状态 dp [i][j] 的计算依赖于前一个物品(i - 1)对应的状态 dp [i - 1][j] 和 dp [i - 1][j - w [i]]。

常见的线性DP问题

LCS问题:

最长公共子序列

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

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

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

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

首先 默认情况下,子数组/字串 是连续的,子序列可以是不连续的

分析:

设长度为n和m

启发思路:

本质上就是选或者不选

特殊的: 考虑最后一对字母,分别为x,y 就有四种情况,都不选,选一个,都选,都不选的话,就从dfs(m,n)的原问题转换成dfs(m-1,n-1)的子问题了,其他的一样 一般化: 考虑s[i]和t[j]选或不选 得到下列一些子问题了

子问题:

s的前i个字母和t的前j个字母的LCS长度

下一个子问题:

s的前i-1个字母和t的前j-1个字母的LCS长度

s的前i-1个字母和t的前j个字母的LCS长度

s的前i个字母和t的前j-1个字母的LCS长度

注: 都不选与都选的情况是一样的,只有s[i]==t[j]的时候才能都选,这种情况下都选肯定比都不选要好,因为要选最长LCS长度嘛。

最终得到下面的一个式子:

dfs(i,j)=max(dfs(i-1,j),dfs(i,j-1),dfs(i-1,j-1)+(s[i]==t[j])

注:

在s[i]==t[j]时,需要调用dfs(i-1,j)和dfs(i,j-1)吗?在在s[i]!=t[j]时要调用dfs(i-1,j-1)吗?

答案是不需要的:

1.设x=dfs(i-1,j-1) 假设dfs(i-1,j)更优,就说明>x+1,也就是大于x,但是后面的又是前面的子序列,是<=x的,就矛盾了,所以第一种情况不需要考虑。

2.假如说只选一个dfs(i-1,j) 再往后面推一次可能就是 dfs(i-1,j-1) 所以说dfs(i-1,j)是大于等于 dfs(i-1,j-1),通俗一点, dfs(i-1,j-1)的结果已经在dfs(i-1,j)里了,所以我们不需要递归到这里

代码实现

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        n=len(text1)
        m=len(text2)
        @cache #记忆化搜索
        def dfs(i,j):
            if i<0 or j<0 :
                return 0
            if text1[i]==text2[j]:
                return dfs(i-1,j-1)+1
            return max(dfs(i-1,j),dfs(i,j-1))

        return dfs(n-1,m-1)

就水一点叭,祝大家新的一年里步步高升!!