代碼隨想錄算法訓練營第60天|動態規劃part17| 647. 回文子串、516.最長回文子序列、動態規劃總結篇

代碼隨想錄算法訓練營第60天|動態規劃part17| 647. 回文子串、516.最長回文子序列、動態規劃總結篇

647. 回文子串

647. 回文子串

思路:

暴力解法

兩層for循環,遍歷區間起始位置和終止位置,然后還需要一層遍歷判斷這個區間是不是回文。所以時間復雜度:O(n^3)

動態規劃

動規五部曲:

  1. 確定dp數組(dp table)以及下標的含義

如果大家做了很多這種子序列相關的題目,在定義dp數組的時候 很自然就會想題目求什么,我們就如何定義dp數組。

絕大多數題目確實是這樣,不過本題如果我們定義,dp[i] 為 下標i結尾的字符串有 dp[i]個回文串的話,我們會發現很難找到遞歸關系。

dp[i] 和 dp[i-1] ,dp[i + 1] 看上去都沒啥關系。

所以我們要看回文串的性質。 如圖:

在這里插入圖片描述

我們在判斷字符串S是否是回文,那么如果我們知道 s[1],s[2],s[3] 這個子串是回文的,那么只需要比較 s[0]和s[4]這兩個元素是否相同,如果相同的話,這個字符串s 就是回文串。

那么此時我們是不是能找到一種遞歸關系,也就是判斷一個子字符串(字符串的下表范圍[i,j])是否回文,依賴于,子字符串(下表范圍[i + 1, j - 1])) 是否是回文。

所以為了明確這種遞歸關系,我們的dp數組是要定義成一位二維dp數組。

布爾類型的dp[i][j]:表示區間范圍[i,j] (注意是左閉右閉)的子串是否是回文子串,如果是dp[i][j]為true,否則為false。

  1. 確定遞推公式

在確定遞推公式時,就要分析如下幾種情況。

整體上是兩種,就是s[i]與s[j]相等,s[i]與s[j]不相等這兩種。

當s[i]與s[j]不相等,那沒啥好說的了,dp[i][j]一定是false。

當s[i]與s[j]相等時,這就復雜一些了,有如下三種情況

情況一:下標i 與 j相同,同一個字符例如a,當然是回文子串
情況二:下標i 與 j相差為1,例如aa,也是回文子串
情況三:下標:i 與 j相差大于1的時候,例如cabac,此時s[i]與s[j]已經相同了,我們看i到j區間是不是回文子串就看aba是不是回文就可以了,那么aba的區間就是 i+1 與 j-1區間,這個區間是不是回文就看dp[i + 1][j - 1]是否為true。

以上三種情況分析完了,那么遞歸公式如下:

if (s[i] == s[j]) {if (j - i <= 1) { // 情況一 和 情況二result++;dp[i][j] = true;} else if (dp[i + 1][j - 1]) { // 情況三result++;dp[i][j] = true;}
}

result就是統計回文子串的數量。

注意這里我沒有列出當s[i]與s[j]不相等的時候,因為在下面dp[i][j]初始化的時候,就初始為false。

  1. dp數組如何初始化

dp[i][j]可以初始化為true么? 當然不行,怎能剛開始就全都匹配上了。

所以dp[i][j]初始化為false。

  1. 確定遍歷順序

首先從遞推公式中可以看出,情況三是根據dp[i + 1][j - 1]是否為true,在對dp[i][j]進行賦值true的。

dp[i + 1][j - 1] 在 dp[i][j]的左下角,如圖:

在這里插入圖片描述
如果這矩陣是從上到下,從左到右遍歷,那么會用到沒有計算過的dp[i + 1][j - 1],也就是根據不確定是不是回文的區間[i+1,j-1],來判斷了[i,j]是不是回文,那結果一定是不對的。

所以一定要從下到上,從左到右遍歷,這樣保證dp[i + 1][j - 1]都是經過計算的。

有的代碼實現是優先遍歷列,然后遍歷行,其實也是一個道理,都是為了保證dp[i + 1][j - 1]都是經過計算的。

for (int i = s.size() - 1; i >= 0; i--) {  // 注意遍歷順序for (int j = i; j < s.size(); j++) {if (s[i] == s[j]) {if (j - i <= 1) { // 情況一 和 情況二result++;dp[i][j] = true;} else if (dp[i + 1][j - 1]) { // 情況三result++;dp[i][j] = true;}}}
}
  1. 舉例推導dp數組

舉例,輸入:“aaa”,dp[i][j]狀態如下:

在這里插入圖片描述
圖中有6個true,所以就是有6個回文子串。

注意因為dp[i][j]的定義,所以j一定是大于等于i的,那么在填充dp[i][j]的時候一定是只填充右上半部分。

代碼:

python

class Solution:def countSubstrings(self, s: str) -> int:dp = [[False] * len(s) for _ in range(len(s))]result = 0for i in range(len(s)-1, -1, -1): #注意遍歷順序for j in range(i, len(s)):if s[i] == s[j]:if j - i <= 1: #情況一 和 情況二result += 1dp[i][j] = Trueelif dp[i+1][j-1]: #情況三result += 1dp[i][j] = Truereturn result

雙指針法

動態規劃的空間復雜度是偏高的,我們再看一下雙指針法。

首先確定回文串,就是找中心然后向兩邊擴散看是不是對稱的就可以了。

在遍歷中心點的時候,要注意中心點有兩種情況。

一個元素可以作為中心點,兩個元素也可以作為中心點。

那么有人同學問了,三個元素還可以做中心點呢。其實三個元素就可以由一個元素左右添加元素得到,四個元素則可以由兩個元素左右添加元素得到。

所以我們在計算的時候,要注意一個元素為中心點和兩個元素為中心點的情況。

這兩種情況可以放在一起計算,但分別計算思路更清晰,我傾向于分別計算,代碼如下:

class Solution:def countSubstrings(self, s: str) -> int:result = 0for i in range(len(s)):result += self.extend(s, i, i, len(s)) #以i為中心result += self.extend(s, i, i+1, len(s)) #以i和i+1為中心return resultdef extend(self, s, i, j, n):res = 0while i >= 0 and j < n and s[i] == s[j]:i -= 1j += 1res += 1return res

516.最長回文子序列

516.最長回文子序列

思路:

動規五部曲分析如下:

  1. 確定dp數組(dp table)以及下標的含義

dp[i][j]:字符串s在[i, j]范圍內最長的回文子序列的長度為dp[i][j]。

  1. 確定遞推公式

在判斷回文子串的題目中,關鍵邏輯就是看s[i]與s[j]是否相同。

如果s[i]與s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;

如圖:

在這里插入圖片描述
(如果這里看不懂,回憶一下dp[i][j]的定義)

如果s[i]與s[j]不相同,說明s[i]和s[j]的同時加入 并不能增加[i,j]區間回文子序列的長度,那么分別加入s[i]、s[j]看看哪一個可以組成最長的回文子序列。

加入s[j]的回文子序列長度為dp[i + 1][j]。

加入s[i]的回文子序列長度為dp[i][j - 1]。

那么dp[i][j]一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);

在這里插入圖片描述
代碼如下:

if (s[i] == s[j]) {dp[i][j] = dp[i + 1][j - 1] + 2;
} else {dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
  1. dp數組如何初始化

首先要考慮當i 和j 相同的情況,從遞推公式:dp[i][j] = dp[i + 1][j - 1] + 2; 可以看出 遞推公式是計算不到 i 和j相同時候的情況。

所以需要手動初始化一下,當i與j相同,那么dp[i][j]一定是等于1的,即:一個字符的回文子序列長度就是1。

其他情況dp[i][j]初始為0就行,這樣遞推公式:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 中dp[i][j]才不會被初始值覆蓋。

vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
  1. 確定遍歷順序

從遞歸公式中,可以看出,dp[i][j] 依賴于 dp[i + 1][j - 1] ,dp[i + 1][j] 和 dp[i][j - 1],如圖:

所以遍歷i的時候一定要從下到上遍歷,這樣才能保證下一行的數據是經過計算的。

代碼如下:

for (int i = s.size() - 1; i >= 0; i--) {for (int j = i + 1; j < s.size(); j++) {if (s[i] == s[j]) {dp[i][j] = dp[i + 1][j - 1] + 2;} else {dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);}}
}
  1. 舉例推導dp數組

在這里插入圖片描述

代碼:

python

class Solution:def longestPalindromeSubseq(self, s: str) -> int:dp = [[0] * len(s) for _ in range(len(s))]for i in range(len(s)):dp[i][i] = 1for i in range(len(s)-1, -1, -1):for j in range(i+1, len(s)):if s[i] == s[j]:dp[i][j] = dp[i+1][j-1] + 2else:dp[i][j] = max(dp[i+1][j], dp[i][j-1])return dp[0][-1]

動態規劃總結篇

動態規劃總結篇

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39691.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39691.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39691.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【mysql】—— 表的增刪改查

目錄 序言 &#xff08;一&#xff09;Create操作 1、單行數據 全列插入 2、多行數據 指定列插入 3、插入否則更新 4、直接替換 &#xff08;二&#xff09;Retrieve操作 1、SELECT 列 1??全列查詢 2??指定列查詢 3??查詢字段為表達式 4??為查詢結果指定…

數據結構——堆

數據結構——堆 堆堆簡介堆的分類 二叉堆過程插入操作 刪除操作向下調整&#xff1a; 增加某個點的權值實現參考代碼&#xff1a;建堆方法一&#xff1a;使用 decreasekey&#xff08;即&#xff0c;向上調整&#xff09;方法二&#xff1a;使用向下調整 應用對頂堆 其他&#…

Python Flask+Echarts+sklearn+MySQL(評論情感分析、用戶推薦、BI報表)項目分享

Python FlaskEchartssklearnMySQL(評論情感分析、用戶推薦、BI報表)項目分享 項目背景&#xff1a; 隨著互聯網的快速發展和智能手機的普及&#xff0c;人們越來越傾向于在網上查找餐廳、購物中心、酒店和旅游景點等商戶的點評和評分信息&#xff0c;以便做出更好的消費決策。…

YOLOv8 : 網絡結構

一. YOLOv8網絡結構 1. Backbone YOLOv8的Backbone同樣參考了CSPDarkNet-53網絡&#xff0c;我們可以稱之為CSPDarkNet結構吧&#xff0c;與YOLOv5不同的是&#xff0c;YOLOv8使用C2f(CSPLayer_2Conv)代替了C3模塊(如果你比較熟悉YOLOv5的網絡結構&#xff0c;那YOLOv8的網絡…

【GitHub】Pycharm本地項目打包上傳到Github倉庫的操作步驟

文章目錄 1、Pycharm端的設置操作2、Github端的設置操作3、Pycharm上配置Github4、Git本地項目至GitHub倉庫5、前往Github中查看確認6、常見報錯 1、Pycharm端的設置操作 通過CtrlAltS快捷組合鍵的方式&#xff0c;打開設置&#xff0c;導航到版本控制一欄中的Git&#xff0c;…

Gin安裝解決國內go 與 熱加載

get 方式安裝超時問題&#xff0c;國內直接用官網推薦的下面這個命令大概率是安裝不成功的 go get -u github.com/gin-gonic/gin 可以在你的項目目錄下執行下面幾個命令&#xff1a; 比如我的項目在E:\Oproject\zl cmd E:\Oproject\zl>就在目錄下執行 go env -w GO111…

回歸預測 | MATLAB實現GRU門控循環單元多輸入多輸出

回歸預測 | MATLAB實現GRU門控循環單元多輸入多輸出 目錄 回歸預測 | MATLAB實現GRU門控循環單元多輸入多輸出預測效果基本介紹程序設計往期精彩參考資料 預測效果 基本介紹 MATLAB實現GRU門控循環單元多輸入多輸出&#xff0c;數據為多輸入多輸出預測數據&#xff0c;輸入10個…

pytorch安裝VAE項目詳解

安裝VAE項目 一、 基本環境二、代碼來源三、搭建conda環境四、下載數據集五、啟動項目六、其他相關問題 一、 基本環境 工具版本號OSwin 11pycharm2020.1GPU3050 二、代碼來源 github地址為&#xff1a; https://github.com/AntixK/PyTorch-VAE/blob/8700d245a9735640dda458d…

ZooKeeper的應用場景(集群管理、Master選舉)

5 集群管理 隨著分布式系統規模的日益擴大&#xff0c;集群中的機器規模也隨之變大&#xff0c;因此&#xff0c;如何更好地進行集群管理也顯得越來越重要了。 所謂集群管理&#xff0c;包括集群監控與集群控制兩大塊&#xff0c;前者側重對集群運行時狀態的收集&#xff0c;后…

08 - 追加commit和修改最新的commit message

查看所有文章鏈接&#xff1a;&#xff08;更新中&#xff09;GIT常用場景- 目錄 文章目錄 1. 追加提交2. 修改最新的commit message 1. 追加提交 將改動追加到上一次的commit 現在我已經修改了Readme文件并且已經add、commit操作&#xff0c;但是還沒有push到遠程倉庫&#x…

【左神算法刷題班】第17節:在有序二維數組中查找目標值、等于目標字符串的子序列個數

第17節 題目1&#xff1a;在有序二維數組中查找目標值 給定一個每一行有序、每一列也有序&#xff0c;整體可能無序的二維數組 再給定一個數num&#xff0c; 返回二維數組中有沒有num這個數 例子 數組如下&#xff0c;找 6 是否存在。 1 3 5 7 2 4 6 13 3 9 14 …

“心理健康人工智能產學研創新聯盟”揭牌成立|深蘭科技

8月14日上午&#xff0c;“2023樹洞救援年會”在上海舉行&#xff0c;會上舉行了“心理健康人工智能產學研創新聯盟”的簽約和揭牌儀式。“樹洞行動救援團”創始人深蘭科技科學院智能科學首席科學家、荷蘭阿姆斯特丹自由大學人工智能系終身教授黃智生&#xff0c;深蘭科技集團創…

華納云:ubuntu啟動寶塔的方法是什么

要在Ubuntu上啟動寶塔面板&#xff08;BT Panel&#xff09;&#xff0c;請按照以下步驟操作&#xff1a; 下載并安裝寶塔面板&#xff1a; 在終端中執行以下命令&#xff0c;以下載并運行寶塔面板的安裝腳本&#xff1a; sudo su wget -O install.sh http://download.bt.c…

Git 常用操作

一、Git 常用操作 1、Git 切換分支 git checkout命令可以用于三種不同的實體&#xff1a;文件&#xff0c;commit&#xff0c;以及分支。checkout的意思就是對于一種實體的不同版本之間進行切換的操作。checkout一個分支&#xff0c;會更新當前的工作空間中的文件&#xff0c;…

68 # 中間層如何請求其他服務

前端 ajax 有跨域問題&#xff0c;可以先訪問中間層&#xff0c;在通過 node 去請求別的服務端口&#xff0c;可以解決跨域問題 編寫中間層調用 // 中間層的方式const http require("http");// http.get 默認發送 get 請求 // http.request 支持其他請求格式 postl…

Java基礎知識實際應用(學生信息管理系統、猜拳小游戲、打印日歷)

一、Java學生信息管理系統 這個系統包含了添加、修改、刪除、查詢和顯示所有學生信息等功能。您可以在此基礎上進行修改和完善&#xff0c;以適應您的需求。 import java.util.Scanner;public class StudentManagementSystem {private static Scanner scanner new Scanner(S…

haproxy負載均衡

1、配置環境 作用環境windows測試  192.168.33.158 172.25.0.11 haproxy負載均衡haproxy&#xff1a;2.8.1&#xff0c;centos7172.25.0.31web服務器1--rs1Apache&#xff1a;2.4&#xff0c;redhat9172.25.0.32web服務器2--rs2Apache&#xff1a;2.4 &#xff0c; redhat9 2、…

Azure使用CLI創建VM

使用CLI創建VM之前&#xff0c;確保資源中的IP資源已經釋放掉了&#xff0c;避免創建的過程中沒有可以利用的公共IP地址打開 cloudshell ,并輸入創建CLI的命令如下&#xff0c;-n指定名稱&#xff0c;-g指定資源組&#xff0c;image指定鏡像&#xff0c;admin-usernam指定用戶名…

【滴滴提前批】移掉 K 位數字

題目 給你一個以字符串表示的非負整數 num 和一個整數 k &#xff0c;移除這個數中的 k 位數字&#xff0c;使得剩下的數字最小。請你以字符串形式返回這個最小的數字。 示例 1 &#xff1a; 輸入&#xff1a;num "1432219", k 3 輸出&#xff1a;"1219&quo…

Maven進階1 -- 分模塊開發、依賴管理、聚合與繼承、屬性、版本管理、多環境開發、跳過測試

目錄 1.分模塊開發 將原始模塊按照功能拆分成若干個子模塊&#xff0c;方便模塊間的相互調用&#xff0c;接口共享。 案例&#xff1a;拆分一下這個SSM整合案例 ①創建maven模塊 demo項目下的pom.xml文件&#xff08;主要看一下依賴&#xff09; <dependencies><!…