程式碼隨想錄演算法訓練營第五十七/天 | 516. 最長迴文子序列,647. 迴文子串

勉g發表於2024-03-25

動態規劃最強總結篇!

如今動態規劃已經講解了42道經典題目,共50篇文章,是時候做一篇總結了。

關於動態規劃,在專題第一篇關於動態規劃,你該瞭解這些! (opens new window)就說了動規五部曲,而且強調了五部對解動規題目至關重要!

這是Carl做過一百多道動規題目總結出來的經驗結晶啊,如果大家跟著「程式碼隨想哦」刷過動規專題,一定會對這動規五部曲的作用感受極其深刻。

動規五部曲分別為:

  1. 確定dp陣列(dp table)以及下標的含義
  2. 確定遞推公式
  3. dp陣列如何初始化
  4. 確定遍歷順序
  5. 舉例推導dp陣列

動規專題剛開始的時候,講的題目比較簡單,不少錄友和我反應:這麼簡單的題目 講的複雜了,不用那麼多步驟分析,想出遞推公式直接就AC這道題目了。

Carl的觀點一直都是 簡單題是用來 鞏固方法論的。 簡單題目是可以靠感覺,但後面稍稍難一點的題目,估計感覺就不好使了。

在動規專題講解中,也充分體現出,這動規五部曲的重要性。

還有不少錄友對動規的理解是:遞推公式是才是最難最重要的,只要想出遞迴公式,其他都好辦。

其實這麼想的同學基本對動規理解的不到位的

動規五部曲裡,哪一部沒想清楚,這道題目基本就做不出來,即使做出來了也沒有想清楚,而是朦朦朧朧的就把題目過了。

  • 如果想不清楚dp陣列的具體含義,遞迴公式從何談起,甚至初始化的時候就寫錯了。
  • 例如動態規劃:不同路徑還不夠,要有障礙! (opens new window)在這道題目中,初始化才是重頭戲
  • 如果看過揹包系列,特別是完全揹包,那麼兩層for迴圈先後順序絕對可以搞懵很多人,反而遞迴公式是簡單的。
  • 至於推導dp陣列的重要性,動規專題裡幾乎每篇Carl都反覆強調,當程式結果不對的時候,一定要自己推導公式,看看和程式列印的日誌是否一樣。

好啦,我們再一起回顧一下,動態規劃專題中我們都講了哪些內容。

#動態規劃基礎

  • 關於動態規劃,你該瞭解這些!(opens new window)
  • 動態規劃:斐波那契數(opens new window)
  • 動態規劃:爬樓梯(opens new window)
  • 動態規劃:使用最小花費爬樓梯(opens new window)
  • 動態規劃:不同路徑(opens new window)
  • 動態規劃:不同路徑還不夠,要有障礙!(opens new window)
  • 動態規劃:整數拆分,你要怎麼拆?(opens new window)
  • 動態規劃:不同的二叉搜尋樹(opens new window)

#揹包問題系列

揹包問題大綱

  • 動態規劃:關於01揹包問題,你該瞭解這些!(opens new window)
  • 動態規劃:關於01揹包問題,你該瞭解這些!(滾動陣列)(opens new window)
  • 動態規劃:分割等和子集可以用01揹包!(opens new window)
  • 動態規劃:最後一塊石頭的重量 II(opens new window)
  • 動態規劃:目標和!(opens new window)
  • 動態規劃:一和零!(opens new window)
  • 動態規劃:關於完全揹包,你該瞭解這些!(opens new window)
  • 動態規劃:給你一些零錢,你要怎麼湊?(opens new window)
  • 動態規劃:Carl稱它為排列總和!(opens new window)
  • 動態規劃:以前我沒得選,現在我選擇再爬一次!(opens new window)
  • 動態規劃: 給我個機會,我再兌換一次零錢(opens new window)
  • 動態規劃:一樣的套路,再求一次完全平方數(opens new window)
  • 動態規劃:單詞拆分(opens new window)
  • 動態規劃:關於多重揹包,你該瞭解這些!(opens new window)
  • 聽說揹包問題很難? 這篇總結篇來拯救你了(opens new window)

#打家劫舍系列

  • 動態規劃:開始打家劫舍!(opens new window)
  • 動態規劃:繼續打家劫舍!(opens new window)
  • 動態規劃:還要打家劫舍!(opens new window)

#股票系列

股票問題總結

  • 動態規劃:買賣股票的最佳時機(opens new window)
  • 動態規劃:本週我們都講了這些(系列六)(opens new window)
  • 動態規劃:買賣股票的最佳時機II(opens new window)
  • 動態規劃:買賣股票的最佳時機III(opens new window)
  • 動態規劃:買賣股票的最佳時機IV(opens new window)
  • 動態規劃:最佳買賣股票時機含冷凍期(opens new window)
  • 動態規劃:本週我們都講了這些(系列七)(opens new window)
  • 動態規劃:買賣股票的最佳時機含手續費(opens new window)
  • 動態規劃:股票系列總結篇(opens new window)

#子序列系列

  • 動態規劃:最長遞增子序列(opens new window)
  • 動態規劃:最長連續遞增序列(opens new window)
  • 動態規劃:最長重複子陣列(opens new window)
  • 動態規劃:最長公共子序列(opens new window)
  • 動態規劃:不相交的線(opens new window)
  • 動態規劃:最大子序和(opens new window)
  • 動態規劃:判斷子序列(opens new window)
  • 動態規劃:不同的子序列(opens new window)
  • 動態規劃:兩個字串的刪除操作(opens new window)
  • 動態規劃:編輯距離(opens new window)
  • 為了絕殺編輯距離,我做了三步鋪墊,你都知道麼?(opens new window)
  • 動態規劃:迴文子串(opens new window)
  • 動態規劃:最長迴文子序列(opens new window)

#動規結束語

關於動規,還有 樹形DP(打家劫舍系列裡有一道),數位DP,區間DP ,機率型DP,博弈型DP,狀態壓縮dp等等等,這些我就不去做講解了,面試中出現的機率非常低。

能把本篇中列舉的題目都研究通透的話,你的動規水平就已經非常高了。 對付面試已經足夠!

這個圖是 程式碼隨想錄知識星球 (opens new window)成員:(opens new window),所畫,總結的非常好,分享給大家。

這應該是全網對動規最深刻的講解系列了。

其實大家去網上搜一搜也可以發現,能把動態規劃講清楚的資料挺少的,因為動規確實很難!要給別人講清楚更難!

《劍指offer》上 動規的題目很少,經典的演算法書籍《演算法4》 沒有講 動規,而《演算法導論》講的動規基本屬於勸退級別的。

講清楚一道題容易,講清楚兩道題也容易,但把整個動態規劃的各個分支講清楚,每道題目講通透,並用一套方法論把整個動規貫徹始終就非常難了。

所以Carl花費的這麼大精力,把自己對動規演算法理解 一五一十的全部分享給了錄友們,幫助大家少走彎路,加油!


647. 迴文子串

已解答
中等

相關標籤

相關企業

提示

給你一個字串 s ,請你統計並返回這個字串中 迴文子串 的數目。

迴文字串 是正著讀和倒過來讀一樣的字串。

子字串 是字串中的由連續字元組成的一個序列。

具有不同開始位置或結束位置的子串,即使是由相同的字元組成,也會被視作不同的子串。

示例 1:

輸入:s = "abc"
輸出:3
解釋:三個迴文子串: "a", "b", "c"

示例 2:

輸入:s = "aaa"
輸出:6
解釋:6個迴文子串: "a", "a", "a", "aa", "aa", "aaa"

提示:

  • 1 <= s.length <= 1000
  • s 由小寫英文字母組成


package main

func countSubstrings(s string) int {
//dp[i][j] 第i為到第j位是否迴文子串
//遞推公式 dp[i][i] if s[i] == s[j] else dp[i-1][i-1] s[i] != s[j] 0 j-i == 0 為1
//初始化 全為0
//從下到上,從左到右
//輸出所有有值的,即為結果
dp := make([][]bool, len(s))
for i := 0; i < len(dp); i++ {
dp[i] = make([]bool, len(s))
}
var res int
for i := len(s) - 1; i >= 0; i-- {
for j := i; j < len(s); j++ {
if i == j {
res++
dp[i][j] = true
continue
}
if s[i] != s[j] {
continue
}
if i+1 == j || dp[i+1][j-1] {
res++
dp[i][j] = true
}
}
}
return res
}

516. 最長迴文子序列

已解答
中等

相關標籤

相關企業

給你一個字串 s ,找出其中最長的迴文子序列,並返回該序列的長度。

子序列定義為:不改變剩餘字元順序的情況下,刪除某些字元或者不刪除任何字元形成的一個序列。

示例 1:

輸入:s = "bbbab"
輸出:4
解釋:一個可能的最長迴文子序列為 "bbbb" 。

示例 2:

輸入:s = "cbbd"
輸出:2
解釋:一個可能的最長迴文子序列為 "bb" 。

提示:

  • 1 <= s.length <= 1000
  • s 僅由小寫英文字母組成


package main

func longestPalindromeSubseq(s string) int {
//dp[i][j] i到j子串最小變動次數
//遞推公式,dp[i][j] if i == j :0 if i+1 = j s[i]==s[j] 0 else 1 s[i] == s[j] = dp[i][j] = dp[i+1][j-1] else min(dp[i+1][j] , dp[i][j-1])+1
//初始化 全為0
//下到上,左到右,
// len(num) - dp[0][len(s)] 為答案
dp := make([][]int, len(s))
fori := 0; i < len(dp); i++ {
dp[i] = make([]int, len(s))
}
fori := len(s) - 1; i >= 0; i-- {
forj := i + 1; j < len(s); j++ {
if i == j {
continue
}
if i+1 == j {
if s[i] != s[j] {
dp[i][j] = 1
}
} else {
if s[i] == s[j] {
dp[i][j] = dp[i+1][j-1]
} else {
dp[i][j] = min(dp[i+1][j], dp[i][j-1])+1
}
}
}
}
returnlen(s) - dp[0][len(s)-1]
}

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

相關文章