leetcode——动态规划

  1. 最大子数组和,必须连续
    输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
    输出:6
    解释:连续子数组 [4,-1,2,1] 的和最大,为 6
在这里插入代码片

300.最长递增/上升子序列
(注意:
子序列:子序列并不要求连续
例如:序列 [4, 6, 5] 是 [1, 2, 4, 3, 7, 6, 5] 的一个子序列;
子串:子串一定是原始字符串的连续子串)

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4

1.
时间复杂度:O(n^2)
空间复杂度: O(n)
dp[i]: i位置处的最长上升子序列长度
func lengthOfLIS(nums []int) int {
    max := 0
    dp := make([]int,len(nums))
    for i := 0; i < len(nums); i++ {
        dp[i] = 1
        for j := 0; j < i; j++ {
            if nums[j] < nums[i] && dp[j]+1 > dp[i] {
                dp[i] = dp[j] + 1
            }
        }
        if max < dp[i] {
            max = dp[i]
        }
    }
    return max
}
2.二分查找

392.判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
输入:s = “abc”, t = “ahbgdc”
输出:true

1.双指针,时间O(m+n),空间O(1)
func isSubsequence(s string, t string) bool {
     i,j := 0,0
     for i < len(s) && j < len(t) {
         if s[i] == t[j] {
             i++
             j++
         }else{
            j++
         }
     }
     return i == len(s)2.进阶:若很长很长呢
//1)dp,时间O(26*n+m),空间O(26*n)
dp[i][j]: t[i]26个字母中出现的位置
//2)哈希+二分查找,哈希表map[byte][]int预处理字符串t,key是字符,value是字符在t中的下标,故value必有序。对s遍历时,二分查找map的value,找出的左边界,即为s[i]在t中第一次出现的位置
func isSubsequence(s string, t string) bool {
   m,n := len(s),len(t)
   if m > n {
       return false
   }
   dp := make([][26]int,n)
   for i := n-1; i >= 0; i-- {
       for j := 0; j < 26; j++ {
           if int(t[i]-'a') == j {
              dp[i][j] = i
           }else if int(t[i]-'a') != j && i == n-1 {
              dp[i][j] = -1
           }else {  //if t[i]-'a' != j {
              dp[i][j] = dp[i+1][j]
           }
       }
   }
   add := 0
   for i := 0; i < m; i++ {
       if add >= n || dp[add][int(s[i]-'a')] == -1 {
           return false
       }else {
           add = dp[add][int(s[i]-'a')] + 1
       }
   }
   return true
}

131.分割回文串
输入:s = “aab”
输出:[ [“a”,“a”,“b”] , [“aa”, “b”]

dp[i][j]: 字符串s[i:j+1]是否为回文串
func partition(s string) [][]string {
//leetcode:5、78
    var (
        n = len(s)
        res [][]string
        path []string
        backtrace func(start int,path []string)
    )
    dp := make([][]bool,n)
    for i := 0; i < n; i++ {
        dp[i] = make([]bool,n)
    }
    backtrace = func(start int,path []string) {
        if start == n {
           res = append(res,append([]string{},path...))
           return
           //等价
           //tmp := []string{}
		   //tmp = append(tmp,path...)
		   //res = append(res,tmp)
           
           //等价
           //tmp := make([]int,len(path))
		   //copy(tmp,path)
		   //res = append(res,tmp)
        }
        for i := start; i < n; i++ {
            if !dp[start][i] { //非回文串时,i++
               continue
            }
            path = append(path,s[start:i+1])
            backtrace(i+1,path)
            path = path[:len(path)-1]
        }
    }
    //dp[i][j]代表:s[i:j+1]是否为回文串
    for j := 0; j < n; j++ {
        for i := 0; i <=j; i++ {
            if s[i] == s[j] && (j-i <= 2 || dp[i+1][j-1]) { 注意:必须j-i <= 2写在前面,否则i=0.j=0时,dp[1][-1]不存在
                dp[i][j] = true
            }
        }
    }
    backtrace(0,path)
    return res
}
var res [][]string
var dp [][]bool
func partition(s string) [][]string {
   res = make([][]string,0)
   path := []string{}
   dp = make([][]bool,len(s))
   for i := 0; i < len(s); i++ {
       dp[i] = make([]bool,len(s))
   }
   for j := 0; j < len(s); j++ {
       for i := 0; i <= j; i++ {
           if s[i] == s[j] && (j-i <= 2 || dp[i+1][j-1]) {
               dp[i][j] = true
           }
       }
   }
   backtrace(0,path,s)   
   return res
}

func backtrace(start int, path []string,s string) {
    if start == len(s) {
        tmp := []string{}
        tmp = append(tmp,path...)
        res = append(res,tmp)
        return
    }
    for i := start; i < len(s); i++ {
        if dp[start][i] {
           path = append(path,s[start:i+1])
           backtrace(i+1,path,s)
           path = path[:len(path)-1]
        }  
    }
}

5.给你一个字符串 s,找到 s 中最长的回文子串。
输入:s = “babad”
输出:“bab”

1.暴力
func longestPalindrome(s string) string {
   n := len(s)
   max := 0
   begin := 0
   for i := 0; i < n; i++ {
       for j := i; j < n; j++ {
           if check(s,i,j) && j-i+1 > max {
              max = j-i+1
              begin = i 
           }
       }
   }
   return s[begin : begin + max]
}

func check(s string, i,j int) bool {
    for i < j {
        if s[i] != s[j] {
            return false
        }
        i++
        j--
    }
    return true
}2.dp
func longestPalindrome(s string) string {
   n := len(s)
   if n < 1 {
       return s
   }

   dp := make([][]bool,n)
   res := ""
   for i := 0; i < n; i++ {
       dp[i] = make([]bool,n)
   }
   for j := 0; j < n; j++ { 
      for i := 0; i <= j ; i++ {
          if s[i] == s[j] && (j-i <= 2 || dp[i+1][j-1]) {
                dp[i][j] = true
          }

          if dp[i][j] && j - i + 1 > len(res) {
             res = s[i:j+1]
          }
      } 
   }
   return res
}

62.不同路径
一个matrix(m行n列),从[0,0]开始,只能向右或者向下走,求走到[m-1, n-1]有多少种方法。即从左上角到右下角
输入:m = 3, n = 7
输出:28

1.时间、空间复杂度:O(mn)
func uniquePaths(m int, n int) int {
    //动态规划,dp[i][j]代表到达(i,j)点的路径数
    //dp := [][]int{}
    dp := make([][]int,m)
    if m == 0 || n == 0 {
        return 1
    }
    for i := 0; i < m; i++ {
        dp[i] = make([]int,n)
        dp[i][0] = 1
    }
    for i := 0; i < n; i++ {
        dp[0][i] = 1
    }
    
    for i := 1; i < m; i++ {
        for j := 1; j < n; j++{
            dp[i][j] = dp[i-1][j] + dp[i][j-1]
        }
    }
    return dp[m-1][n-1]
}
2.dp利用滚动数组,改为一维数组,空间O(n)
func uniquePaths(m int, n int) int {
    dp := make([]int,n)
    for i := 0; i < n; i++ {
        dp[i] = 1
    }
    for i := 1; i < m; i++ {
        for j := 1; j < n; j++ {
            dp[j] = dp[j-1] + dp[j]
        } 
    }
    return dp[n-1]
}

121.买卖股票的最佳时机 = 找出数组中的两个数,其差值要最大
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

func maxProfit(prices []int) int {
//通过率是67%的童鞋是只考虑了股价为个位数的情况吧
//dp[i]:到第i天为止的最大利润
//dp[i] = max(今日若买入的最大利润,最大利润)
//dp[i] = max(price[i] - minBuyPrice, dp[i-1])
    minBuyPrice := prices[0]
    dp := make([]int,len(prices))
    for i := 1; i < len(prices); i++ {
        dp[i] = max(prices[i]-minBuyPrice,dp[i-1])
        if prices[i] < minBuyPrice {
           minBuyPrice = prices[i]
        }
    }
    return dp[len(prices)-1]
}

func max(a,b int) int {
    if a > b {
        return a
    }
    return b
}

121.买卖股票的最佳时机,可以进行多次交易

func maxProfit(prices []int) int {
//3.动态规划
//dp[i][0] 表示第i天交易完后手里没有股票时,我拥有的最大现金数
//dp[i][1] 表示第i天交易完后手里持有一支股票时,我拥有的最大现金数
    n := len(prices)
    dp := make([][2]int,n)
    /*for i := 0; i < n; i++ {
        dp[i] = make([]int,2)
    }*/
    dp[0][0] = 0
    dp[0][1] = -prices[0]
    for i := 1; i < n; i++ {
        //前一天有股票,今天卖,持有现金 = 加法运算
        //前一天没股票,今天买,持有现金 = 减法运算
        dp[i][0] = max( dp[i-1][0], dp[i-1][1] + prices[i] ) 
        dp[i][1] = max( dp[i-1][1], dp[i-1][0] - prices[i] ) 
    }
    return dp[n-1][0] //最后手里不能有股票
}
func max(a,b int) int {
    if a > b {
        return a
    }
    return b
}

/*1.贪心:把所有的上坡全部收集到
func maxProfit(prices []int) int {
    ans := 0
    for i := 1; i < len(prices); i++ {
        if prices[i] - prices[i-1] > 0 {
            ans += prices[i] - prices[i-1]
        }
    }
    return ans
}*/

/*2.寻找到买入时,哪天卖出会使利润最大
func maxProfit(prices []int) int {
   buyIn,sellOff := prices[0],prices[0]
   sumProfit,curMaxProfit := 0,0
   for i := 1; i < len(prices); i++ {
       if prices[i] > buyIn && prices[i] - buyIn >curMaxProfit {
          curMaxProfit = prices[i] - buyIn
          sellOff = prices[i]
       }
       if prices[i] < sellOff {
           sumProfit += curMaxProfit
           buyIn,sellOff = prices[i],prices[i]
           curMaxProfit = 0
       }
   }  
   if sellOff == prices[len(prices)-1] {
       sumProfit += curMaxProfit
   }
   return sumProfit
}*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值