dp问题汇总(持续更)

Leetcode上看到这么一句话

dynamic programming is basically recursion with caching

这和之前的一些做题经历比较契合。

dp,重要的是找递推关系 dp[i] = f(dp[i-1]) 或者 dp[i][j] = f(dp[i-1][j], dp[i][j-1])等等

但是有时候关系没有那么好找到

也有的时候不会第一时间意识到这是dp问题

我发现,dp往往会涉及最大(最小)值的问题。适合的就是记截止到第i步的最值为dp[i]

当然有时候更容易想到递归,但是递归会涉及到很多的重复计算,往往会超时。如果能够将一些递归的结果存储起来,避免重复计算,会减少不少用时,但还是会涉及层层函数调用,

依旧没有dp来的那么划算。尽管如此,还是可以从递归入手,去启发找到dp的递推关系。可能自上而下比自上而下好想一点吧

另外就是要关注递归的结束条件,也就是dp数组的初始化行&列,有时会比较特殊。

例子:leetcode72 计算两字符串的edit distance

递归思路算是最好想的

class Solution:
    def minDistance(self, word1, word2):
        """Naive recursive solution"""
        if not word1 and not word2:
            return 0
        if not word1:
            return len(word2)
        if not word2:
            return len(word1)
        if word1[0] == word2[0]:
            return self.minDistance(word1[1:], word2[1:])
        insert = 1 + self.minDistance(word1, word2[1:])
        delete = 1 + self.minDistance(word1[1:], word2)
        replace = 1 + self.minDistance(word1[1:], word2[1:])
        return min(insert, replace, delete)

有字典存储结果避免重复计算的

class Solution:
    def minDistance(self, word1, word2, i, j, memo):
        """Memoized solution"""
        if i == len(word1) and j == len(word2):
            return 0
        if i == len(word1):
            return len(word2) - j
        if j == len(word2):
            return len(word1) - i

        if (i, j) not in memo:
            if word1[i] == word2[j]:
                ans = self.minDistance2(word1, word2, i + 1, j + 1, memo)
            else: 
                insert = 1 + self.minDistance2(word1, word2, i, j + 1, memo)
                delete = 1 + self.minDistance2(word1, word2, i + 1, j, memo)
                replace = 1 + self.minDistance2(word1, word2, i + 1, j + 1, memo)
                ans = min(insert, delete, replace)
            memo[(i, j)] = ans
        return memo[(i, j)]

自下而上dp就很简单了

class Solution:
    def minDistance(self, word1, word2):
        """Dynamic programming solution"""
        m = len(word1)
        n = len(word2)
        table = [[0] * (n + 1) for _ in range(m + 1)]

        for i in range(m + 1):
            table[i][0] = i
        for j in range(n + 1):
            table[0][j] = j

        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if word1[i - 1] == word2[j - 1]:
                    table[i][j] = table[i - 1][j - 1]
                else:
                    table[i][j] = 1 + min(table[i - 1][j], table[i][j - 1], table[i - 1][j - 1])
        return table[-1][-1]

 

你可能感兴趣的:(Python,leetcode,动态规划,算法)