- 最大子数组和,必须连续
输入: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
}*/