BM79 打家劫舍(二)、BM65 最长公共子序列(二)、BM66 最长公共子串、BM68 矩阵的最小路径和、BM69 把数字翻译成字符串

1.BM79 打家劫舍(二)

题:为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家,如果偷了第二家,那么就不能偷第一家和第三家。且第一个房间和最后一个房间视为相邻。计算在不被发现的前提下最多的偷窃金额。

#对于一个人家,我们选择偷他或者不偷他,如果我们选择偷那么前一家必定不能偷;如果选择不偷他,那我们最多可以取得上一级的收益
#移方程为dp[i] = max(dp[i - 1], nums[i - 1] + dp[i - 2])
#既然是个环,那么第一家和最后一家不能同时偷。所以要分两种情况。

class Solution:
    def rob(self , nums: List[int]) -> int:
        dp = [0] * len(nums)
        dp[0],dp[1]=nums[0],max(nums[:2])
        for i in range(2,len(nums)-1):#情况一:偷了第一家,不能偷最后一家
            dp[i]=max(dp[i-1],dp[i-2]+nums[i])
        res1=dp[len(nums)-2]#记录情况一这样偷的最大值
        dp[0],dp[1]=0,nums[1]
        for i in range(2,len(nums)):#情况二:不偷第一家,选择最后一家是否同偷
            dp[i]=max(dp[i-1],dp[i-2]+nums[i])
        return max(res1,dp[len(nums)-1])
        

 2. 给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。

#遍历两个字符串的所有位置比较 dp[i][j]表示在s1中以i结尾,s2中以j结尾的字符串的最长公共子序列
#状态转移:

  • i位与j位的字符相等,则该问题可以变成dp[i-1][j-1]+s[i]
  • 不相同,因此我们考虑,dp[i][j-1]或者dp[i-1][j],取较大的一个
#优化 因为dp[i][j]只和dp[i][j-1]、dp[i-1][j]有关 空间复杂度可再降低
class Solution:
    def LCS(self , s1: str, s2: str) -> str:
        s1=' '+s1#既防止特例 又不用管第一个和第一个比较
        s2=' '+s2
        n,m=len(s1),len(s2)
        pre=['']*m
        for i in range(1,n):
            cur=['']*m #相当于dp的行向量每次都初始化
            for j in range(1,m):
                if s1[i]==s2[j]:
                    cur[j]=pre[j-1]+s1[i]
                else:
                    if len(pre[j])>len(cur[j-1]):
                        cur[j]=pre[j]
                    else:
                        cur[j]=cur[j-1]
            pre=cur #记录上一个行向量dp[i-1][:]
        res=cur[-1] if len(cur[-1]) else '-1'
        return res
#不优化版
class Solution:
    def LCS(self , s1: str, s2: str) -> str:
        s1=' '+s1#既防止特例 又不用管第一个和第一个比较
        s2=' '+s2
        n,m=len(s1),len(s2)
        dp=[['']*m for _ in range(n)]
        for i in range(1,n):
            for j in range(1,m):
                if s1[i]==s2[j]:
                    dp[i][j]=dp[i-1][j-1]+s1[i]
                else:
                    if len(dp[i-1][j])>len(dp[i][j-1]):
                        dp[i][j]=dp[i-1][j]
                    else:
                        dp[i][j]=dp[i][j-1]
        res=dp[-1][-1] if len(dp[-1][-1]) else '-1'
        return res

 3.BM66 最长公共子串

题:给定两个字符串str1和str2,输出两个字符串的最长公共子串。题目保证str1和str2的最长公共子串存在且唯一

 注意这题求的是最长公共子串,不是最长公共子序列,子序列可以是不连续的,但子串一定是连续的。

#暴力枚举法 
class Solution:
    def LCS(self , str1: str, str2: str) -> str:
        #让str1为较长的字符串
        if len(str1) < len(str2): 
            str1, str2 = str2, str1
        res = ''
        max_len = 0
        #遍历str1的长度
        for i in range(len(str1)): 
            #查找是否存在 存在的话就一直往后移动 !最大子串唯一
            if str1[i - max_len : i + 1] in str2: 
                res = str1[i - max_len : i + 1]#最大子串
                max_len += 1#相同的最大长度
        return res

 动态规划

#dp[i][j]表示在str1中以第i个字符结尾,str2中以第j个字符结尾,公共子串的长度
#优化版 把dp[i][j]=cur[j] dp[i-1][j]=pre[j]

class Solution:
    def LCS(self , str1: str, str2: str) -> str:
        str1=' '+str1#既防止特例 又不用管第一个和第一个比较
        str2=' '+str2
        n,m=len(str1),len(str2)
        dp=[[0]*m for _ in range(n)]
        ma=0
        for i in range(1,n):
            for j in range(1,m):
                if str1[i]==str2[j]:
                    dp[i][j]=dp[i-1][j-1]+1
                else:
                    dp[i][j]=0
                #更新最大长度 和最大长度所在位置
                if dp[i][j]>ma:
                    ma=dp[i][j]
                    re=i #在i这个位置结束相同 (此后ma,re没有再更新过)
        return str1[re-ma+1:re+1]

#直接判断 注意 子序列不连续,子串是连续的
#遍历str1中的字符 构成子串 判断是否在str2中

class Solution:
    def LCS(self, str1, str2):
        a,res = '',''
        for i in str1:
            a = a + i
            if a in str2:
                res = a
            else:
                a = a[1:]#子串往后走
        return res

 4.BM68 矩阵的最小路径和

题:给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。

 #状态转移公式为dp[i][j]=min(dp[i−1][j],dp[i][j−1])+matrix[i][j]
#优化 空间优化 因为状态转移方程不涉及走过的matrix[i][j]的值,因此可以使用matrix代替dp

class Solution:
    def minPathSum(self , matrix: List[List[int]]) -> int:
        n,m=len(matrix),len(matrix[0])
        #dp=matrix
        for j in range(1,m):#初始化第一行
            matrix[0][j]=matrix[0][j]+matrix[0][j-1]
        for i in range(1,n):#初始化第一列
            matrix[i][0]=matrix[i][0]+matrix[i-1][0]
        for i in range(1,n):#遍历矩阵
            for j in range(1,m):
                 matrix[i][j]=matrix[i][j]+min(matrix[i-1][j],matrix[i][j-1])
        return matrix[-1][-1]

 5.BM69 把数字翻译成字符串

 对于一个数,我们可以直接译码它,也可以将其与前面的组合起来译码

dp[i]=dp[i−1]+dp[i−2]

或 dp[i]=dp[i−2]

class Solution:
    def solve(self , nums: str) -> int:
        dp=[0]*3
        if nums[0] =='0':return 0
        dp[0]=1
        dp[1]=1
        for i in range(1,len(nums)):
            if nums[i]!="0":#可以直接译 
                dp[2]=dp[1]
            else:
                dp[2]=0
            #那到底能和前面那个在一起译么
            if 10 <= int(nums[i-1:i+1])<= 26:#可以和前面那个在一起译
                dp[2]+=dp[0]
            dp[1],dp[0]=dp[2],dp[1]    
        return dp[1]#不输出dp[2]是为保证n=1时有输出

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