LeetCode分类02:动态规划

动态规划

动态规划常适用于有重叠子问题和最优子结构性质的问题,它的本质是以空间换取时间。
它的模板步骤如下:

  • 确定动态规划状态;
  • 写状态转移方程(画状态转移表);
  • 考虑初始化条件;
  • 考虑输出;
  • 考虑对时间,空间复杂度的优化(较难)

按上述步骤进行详细叙述:

  1. 思考状态
    状态的定义,先试着将题目所求的设置为状态;然后思考状态是如何转移的,如果状态转移方程不易得到,尝试修改定义,目的依然是为了方面得到状态转移方程。
  2. 思考状态转移方程
  3. 思考初始化
  4. 思考输出

对于Q1:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

从回文串的定义思考:如果一个字符串的头尾两个字符都不相等,那么这个字符串一定不是回文串;
如果一个字符串的头尾两个字符相等,进一步讨论:如果里面的字串是回文,则整体是回文串;如果里面的子串不是回文串,整体就不是回文串。
头尾字符相等的情况下,里面子串是否为回文串决定了整个子串是否为回文串,因此可以将状态定义为远字符串的一个子串是否为回文子串。
定义状态
dp[i][j]表示子串s[i...j]是否为回文子串,这里子串s[i...j]为闭区间。
思考状态转移方程
动态规划实际上相当于填一张二维表格,由于考虑子串,因此,i 小于等于j,也就是只需要填表格的右上部分。
边界条件:如果表达式[i+1 , j-1]不构成区间,即长度严格小于2,即j-1-(i+1)+1<2,整理得j-i<3
这个结论很显然:当子串s[i...j]的长度等于2或者3的时候,只需要判断一下头尾两个字符是否相等可直接下结论。
如果子串s[i+1...j-1]只有1个字符,即去掉两头,剩下中间部分只有1个字符,显然是回文;
如果子串s[i+1...j-1]为空串,那么子串s[i , j]一定是回文子串。
因此,在s[i] == s[j],成立j-i<3的前提下,直接可以下结论,dp[i][j] = true,否则才执行状态转移。
** 考虑初始化**
初始化的时候,单个字符一定是回文串,因此把对角线先初始化为true,即dp[i][j] = true
考虑输出
当得到dp[i][j] = true,就立即记录子串的长度和起始位置。

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        n = len(s)
        if n <2:
            return s
        
        start = 0
        max_len = 1

        dp = [[False for _ in range(n)] for _ in range(n)]
        for i in range(n):
            dp[i][i]=True

        for j in range(1,n):
            for i in range(j):
                if s[i] == s[j]:
                    if j-i+1 <=3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i+1][j-1]
                else:
                    dp[i][j]=False

                if dp[i][j]:
                    cur_len = j-i+1
                    if cur_len>max_len:
                        max_len = cur_len
                        start = i

        return s[start:start+max_len]

对于Q2:给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

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

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

在这里插入图片描述
定义状态

dp[i][j]定义为word1中前i个字符转化成word2中前j个字符需要的最少步数。

思考状态转移方程
word1[i] == word2[j]时,dp[i][j] = dp[i-1][j-1]
word1[i] != word2[j]时,dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
其中,dp[i-1][j-1]代表替换操作,dp[i-1][j]表示删除操作,dp[i][j-1]表示增加操作。
初始化

dp[i][j] = 0

class Solution(object):
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """

        m = len(word1)
        n = len(word2)
        # 加了边界条件,多加一行一列
        dp = [[0 for _ in range(n+1)] for _ in range(m+1)]

        for i in range(n+1):
            dp[0][i] = i # 对于第一行,每次都是在之前基础上添加一个操作
        for j in range(m+1):
            dp[j][0] = j #第一列也是
        for 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][j-1],dp[i-1][j],dp[i-1][j-1])+1

        return dp[-1][-1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值