leetcode 5 最长回文子串 动态规划 python深浅复制

leetcode 5 最长回文子串 动态规划 python深浅复制

知识准备——动态规划

动态规划思想本质上是分治与递归

四大部分

  1. 划分状态,即将父问题划分为可以递归的若干相同模式的子问题。
  2. 状态表示,即将子问题用计算机可以理解的逻辑符号(函数等)或程序代码表示。
  3. 状态转移,即确定父问题和子问题之间的关系,列出状态转移方程。
  4. 确定边界,即确定最小的子问题、最大的父问题、初始状态、最终状态。

经典模型

  1. 线性模型(斐波那契数列问题)
  2. 区间模型(最大回文串问题)
  3. 树状模型(公司优秀员工问题)

解法

  1. 自顶向下(记忆化搜索),即从最终状态出发,若子问题已经有解,那么直接拿来用,若子问题无解,那么转化为求解子问题,一般采用递归方式求解。适合树/图状结构的问题。
  2. 自底向上,即从初始状态出发,根据已经确定的拓扑序一步步求解子问题,直到达到最终状态。适合线性模型等可以简单确定拓扑序的问题。

题目分析

在本题中,划分状态为s中从i位置到j位置的子串是否为回文串;状态表示为布尔值数组dp[i][j];状态转移方程为dp[i][j]=dp[i+1][j-1] and (s[i]==s[j]);确定边界为若i==j,dp[i][j]=True,若i+1==j and s[i]==s[j],dp[i][j]=True;解法为自底向上,按照长度从小到大枚举所有的子串,某长度的子串是建立在长度小于它的子串的判断结果上的。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        res = ""
        max = 0
        dp = [[False]*n for _ in range(n)]
        for l in range(len(s)):
            for i in range(len(s)):
                j=i+l
                if j>=len(s):
                    break
                if i==j:
                    dp[i][j]=True
                elif i==j-1:
                    dp[i][j]=(s[i]==s[j])
                else:
                    dp[i][j]=(dp[i+1][j-1] and s[i]==s[j])
                if dp[i][j] and j-i+1>max:
                    max = j-i+1
                    res = s[i:j+1]
        return res

6464ms 24.1MB

深复制与浅复制

在创建n*n的数组dp时,首先我的写法是

dp = [[False]*len(s)]*len(s)

但是这种写法是会报错的,猜测可能是python中的安全性保护措施,改为

n = len(s)
dp = [[False]*n]*n

即不报错,但是这种写法仍然是有问题的

python中的内存和变量的关系有深复制和浅复制之分,对于元组

a = [1,2,[3,4]]

假如通过浅复制将a复制到b中,那么对于首层的元素,他们的内存地址是分别独立的,即更改b[0],b[1],b[2]的值,a[0],a[1],a[2]的值不会随之改变,但是更改b[2][0],b[2][1]的值,a[2][0],a[2][1]的值则会随之改变,即首层以下的元素,他们是同一内存地址,互为引用

假如通过深复制,那么对于任意一层的元素,他们的内存地址都是分别独立

因此这种写法会导致dp数组实际上每一行的内存地址是相同的,*是一种浅复制

解决措施:不使用*号,这将导致浅复制,乖乖使用枚举所有元素的方法初始化元组,这里可以使用 for _ 语句

n = len(s)
dp = [[False for _ in range(n)] for _ in range(n)]

当然由于只有两层,也可以写成

n = len(s)
dp = [[False]*n for _ in range(n)]

另外也可以使用numpy进行初始化,leetcode也支持

n = len(s)
a = np.zeros(shape=(n,n),dtype=bool)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值