代碼隨想錄算法訓練營第60期第四十九天打卡

? ? ? ?大家好,今天我們還是繼續我們的動態規劃章節,可能有的朋友已經開始厭倦了說為什么動態規劃怎么這么多題目,大家可以想想我們前面其實刷過好幾種類型的動態規劃的經典題目比如說各式各樣的背包問題當然包括0-1背包,完全背包,大家也知道這兩種背包問題的差異就在于物品是否可以選擇多次,后面我們還刷了打家劫舍問題,當然還有比較難的買股票的最佳時期問題,前天我們也開始我們的子序列問題,那我們今天還有一種比較重要的問題就是編輯距離問題,那我們就一起走進我們今天的題目。

第一題對應力扣編號為115的題目不同的子序列

? ? ? ?其實大家看到這道題的時候大家應該會想起我們昨天刷過一道題目叫做判斷子序列,其實我可以告訴大家,這道題要比那道題難一些,我們來看看這道題的題目要求:

? ? ? ? 看完題目我們明白了,其實跟那道題有點類似的地方就是都涉及到了子序列的問題,但是這道題我們是需要求出s的子序列中t出現的次數,這個其實就有難度了,當然我們也知道本題目應該是可以使用動態規劃的思路來解題的,那我們就嘗試使用動規五部曲來解決這道題目:

? ? ? ?第一步確定dp數組以及下標的含義,dp[i][j]:以i-1為結尾的s子序列中出現以j-1為結尾的t的個數為dp[i][j]。我們前面有道題說過為了初始化方便我們是i-1而不是i。

? ? ? ? 第二步確定遞推公式,感覺這個就有難度了,其實我們做的題目多了就會有感覺,我們應該還是需要看對應位置上的元素是否相等,第一種情況是s[i - 1] 與 t[j - 1]相等,第二種情況是s[i - 1] 與 t[j - 1] 不相等,當s[i - 1] 與 t[j - 1]相等時,dp[i][j]可以有兩部分組成。一部分是用s[i - 1]來匹配,那么個數為dp[i - 1][j - 1]。即不需要考慮當前s子串和t子串的最后一位字母,所以只需要 dp[i-1][j-1]。還有一種情況就是一部分是不用s[i - 1]來匹配,個數為dp[i - 1][j]。另一種情況就是s[i - 1] 與 t[j - 1]不相等時,我們這時候其實相當于dp[i][j]只有一部分組成,不用s[i - 1]來匹配(就是模擬在s中刪除這個元素),即:dp[i - 1][j]。

? ? ? ?第三步是dp數組的初始化,從遞推公式dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; 和 dp[i][j] = dp[i - 1][j]; 中可以看出dp[i][j] 是從上方和左上方推導而來,那么 dp[i][0] 和dp[0][j]是一定要初始化的。那其實也很明顯,我們的dp[i][0]應該初始化為1,而且我們的dp[0][j]應該初始化為0,但是大家要注意我們dp[0][0]應該是初始化為1,因此我們dp[0][j]的初始化應該是從1開始初始化為0.

? ? ? ?第四步就是遍歷順序的確定,其實很明顯就是從前往后,這個就無需多說了,其實嚴格來說應該是左上方和正上方推出來的。

? ? ? 根據以上分析我們可以給出代碼:

class Solution {
public:int numDistinct(string s, string t) {//初始化dp數組vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1));//dp數組初始化for (int i = 0; i <= s.size(); ++i) dp[i][0] = 1;for (int j = 1; j <= t.size(); ++j) dp[0][j] = 0;for (int i = 1; i <= s.size(); ++i){for (int j = 1; j <= t.size(); ++j){//如果相同存在兩種情況第一種用最后一個元素第二種不用最后一個元素if (s[i - 1] == t[j - 1]){dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];}else{//如果不相等就得模擬刪除當前最后一個元素dp[i][j] = dp[i - 1][j];}}}return dp[s.size()][t.size()];}
};

? ? ? ?其實本題的難點就在于理解狀態轉移方程,我們本題的dp數組的含義其實還是比較抽象的,我們要比較對應位置的元素是否相等,如果相等的話我們應該如何操作,如果不相等的話我們應該如何操作。其實我一想了好久才大致明白我們的狀態轉移,我開始也思考了為什么我們不考慮用不用t[j-1]后來發現是因為我沒有仔細審題,題目問的是s中有多少個t,所以重點考慮的應該是s[i-1],還有如果我們對應的元素不相等,其實我們相當于模擬刪除s里面的s[i-1]因此我們這里應該是dp[i-1][j],相等的話我們則不需要考慮兩個字符串的最后一個元素,但是不相等的話我們的確是需要考慮。這點大家要多思考。

第二題對應力扣編號為583的題目兩個字符串的刪除操作

? ? ? ? 我們來到今天的第二題,第一題真給我差點整不會了,大家務必要仔細思考一下,我們來看今天的第二題是什么意思:

? ? ? ?其實題目理解起來并不算難,就是我們要使得兩個字符串相同我們就需要進行增加刪除操作,我們需要求我們最少的操作數是多少,我們還是得使用動規五部曲的思路來解決這道題目:

? ? ? ? 第一步確定dp數組以及下標的含義,我們應該是dp[i][j]:以i-1為結尾的字符串word1,和以j-1位結尾的字符串word2,想要達到相等,所需要刪除元素的最少次數。這個定義起來其實不難,但是難的應該是遞推公式。

? ? ? ? 第二步確定遞推公式,同樣我們還是得考慮對應位置的元素是否相等,也就是當word1[i - 1] 與 word2[j - 1]相同的時候,和當word1[i - 1] 與 word2[j - 1]不相同的時候,先來看第一種情況就是對應位置元素相同的時候,當word1[i - 1] 與 word2[j - 1]相同的時候,dp[i][j] = dp[i - 1][j - 1],這個其實好理解,對應位置相同我們這里的元素無需操作只需看前面即可,但是比較難的是第二種情況,這個其實又有好幾種情況,情況一:刪word1[i - 1],最少操作次數為dp[i - 1][j] + 1,刪word2[j - 1],最少操作次數為dp[i][j - 1] + 1,同時刪word1[i - 1]和word2[j - 1],操作的最少次數為dp[i - 1][j - 1] + 2,那最后當然是取最小值,所以當word1[i - 1] 與 word2[j - 1]不相同的時候,遞推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1}),其實這里想一次性想得很全面我個人感覺是比較難的。

? ? ? ? 第三步dp數組的初始化,從遞推公式中,可以看出來,dp[i][0] 和 dp[0][j]是一定要初始化的。但其實似乎又不難理解,dp[i][0]:word2為空字符串,以i-1為結尾的字符串word1要刪除多少個元素,才能和word2相同呢,很明顯dp[i][0] = i。dp[0][j]的話同理,不應該是刪除j個因此dp[0][j]=j,這樣我們的初始化就完成了。

? ? ? ? 第四步確定遍歷順序,這個其實也毫無疑問,從遞推公式 dp[i][j] = min(dp[i - 1][j - 1] + 2, min(dp[i - 1][j], dp[i][j - 1]) + 1); 和dp[i][j] = dp[i - 1][j - 1]可以看出dp[i][j]都是根據左上方、正上方、正左方推出來的。

? ? ? ? 那通過以上分析我們就可以嘗試給出本題的解題代碼:

class Solution {
public:int minDistance(string word1, string word2) {//定義dp數組vector<vector<int>> dp(word1.size() + 1,vector<int>(word2.size() + 1));//dp數組的初始化for (int i = 0; i <= word1.size(); ++i) dp[i][0] = i;for (int j = 0; j <= word2.size(); ++j) dp[0][j] = j; for (int i = 1; i <= word1.size(); ++i){for (int j = 1; j <= word2.size(); ++j){if (word1[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1];else{dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j - 1] + 2));}}}return dp[word1.size()][word2.size()];}
};

? ? ? ? 其實本題的難點在于考慮的情況是否周全,還是思路要理清。接下來就是重頭戲編輯距離題目,我們這道題目就分享到這里。

第三題對應力扣編號為72的題目編輯距離

? ? ? ? 在刷題過程中我似乎記得我們前面刷的題都是為這個題目服務的,其實上一個題目我們只涉及到了字符串的刪除沒有涉及到替換添加等操作,那我們就一起來看看這道題目的具體要求:

? ? ? ?我們其實是求將前面的字符串變成后面字符串的的最少操作數,當然本題目我們可以進行替換刪除插入操作,而我們前面的題目只能進行刪除操作,那我們需要考慮的情況可能就更多了,我們就使用動規五部曲來解決一下這道題目:

? ? ? ?第一步就是確定?確定dp數組以及下標的含義,其實這個似乎不難,我們完全有經驗,dp[i][j] 表示以下標i-1為結尾的字符串word1,和以下標j-1為結尾的字符串word2,最近編輯距離為dp[i][j]。跟我們以前的題目定義dp數組的思路是完全類似的。

? ? ? ?第二步就是確定遞推公式,這個或許有難度,這個題如果我們要操作的話我們就必須考慮插入刪除替換等各種操作,如果對應元素相同那么就不需要任何的操作,但是如果不同的話就麻煩了,其實有如下幾個操作,操作一:word1刪除一個元素,那么就是以下標i - 2為結尾的word1 與 j-1為結尾的word2的最近編輯距離 再加上一個操作。操作二:word2刪除一個元素,那么就是以下標i - 1為結尾的word1 與 j-2為結尾的word2的最近編輯距離 再加上一個操作。這里就有問題了為什么都是刪除元素為什么沒有添加元素的操作,其實大家可以變相思考一下,我word2添加一個元素,相當于word1刪除一個元素,比如說word1 = "ad", word2 = "a",那我word1刪除一個"d"變成"a",與word2添加一個"d",變成"ad"的最后的操作數應該是一樣的,操作三:替換元素,這個其實word1替換word1[i - 1],使其與word2[j - 1]相同,?那么只需要一次替換的操作,就可以讓 word1[i - 1] 和 word2[j - 1] 相同,因此這時候dp[i][j] = dp[i-1][j - 1] +1,那這樣我們的遞推公式就知道了,就是分為對應元素相等跟不相等的情況。

? ? ? 第三步就是dp數組的初始化,應該重點思考dp[i][0] 和 dp[0][j] 表示什么?其實也不難,dp[i][0]就應該是i,對word1里的元素全部做刪除操作,即:dp[i][0] = i; 同理dp[0][j] = j; 這個其實我們上一道題目就涉及到了。

? ? ? 第四步就是遍歷順序,其實我們也不多說了跟上一道題目是完全一樣的。

? ? ? 這樣經過我們的分析我們就給出本題的解題代碼:

class Solution {
public:int minDistance(string word1, string word2) {//初始化dp數組vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1));//dp數組初始化for (int i = 0; i <= word1.size(); ++i) dp[i][0] = i;for (int j = 0; j <= word2.size(); ++j) dp[0][j] = j;for (int i = 1; i <= word1.size(); ++i){for (int j = 1; j <= word2.size(); ++j){if (word1[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1];else dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1));}}return dp[word1.size()][word2.size()];}
};

? ? ? ? 這道題目很有趣,完全就是動態規劃的典型例題,大家一定要把這道題目掌握扎實,我感覺好多公司面試都會考察這道題目,其實我給大家一個總結大家重點首先理解dp數組,其次搞清楚我們需要什么樣的操作就可以了,有的我們只可以進行刪除操作,有的我們增刪改查都可以,而且大家要明白每一種操作我們的狀態轉移方程應該如何表示就可以,其實我們前面的幾道題都是為了編輯距離這道題目服務的,可是說是絕殺。大家務必學明白!!

今日總結

? ? ? ? 我們即將到達動態規劃的終結篇了,我們還有單調棧跟圖論我們的訓練營就結束了,大家收獲一定是滿滿的,我們在眾多章節收獲了很多,大家需要二刷甚至三刷才可以,重要的是理解思想,多去思考多去嘗試就可以,我們今天的分享就到這里,大家明天見!

? ? ? ?

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

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

相關文章

centos7.9離線升級內核到4.19.12詳細教程

cenots7.9默認安裝的內核版本是:3.10.0-1160.119.1.el7.x86_64,在安裝nvidia顯卡驅動的時候,提示內核版本過低,需要升級內核版本,升級完成之后,就可以順利的安裝上nvidia顯卡驅動了,實測有效。 一、查看當前內核版本命令 uname -r二、下載離線內核的rpm包

Vue3 + TypeScript + el-input 實現人民幣金額的輸入和顯示

輸入人民幣金額的參數要求&#xff1a; 輸入要求&#xff1a; 通過鍵盤&#xff0c;只允許輸入負號、小數點、數字、退格鍵、刪除鍵、方向左鍵、方向右鍵、Home鍵、End鍵、Tab鍵&#xff1b;負號只能在開頭&#xff1b;只保留第一個小數點&#xff1b;替換全角輸入的小數點&a…

方正字庫助力華為,賦能鴻蒙電腦打造全場景字體解決方案

2025年5月19日&#xff0c;搭載華為鴻蒙操作系統的鴻蒙電腦&#xff0c;面向用戶推出集AI智能、互聯流暢、安全保障和精致體驗于一體的全新辦公系統。作為鴻蒙生態核心字體服務商&#xff0c;方正字庫為此次提供了全面的系統字體支持&#xff0c;涵蓋中文、西文及符號三大類字庫…

PHPStudy 一鍵式網站搭建工具的下載使用

目錄 一、下載 PHPStudy二、安裝步驟三、基本使用方法3.1 創建網站3.2 管理數據庫3.3 軟件管理3.4 自動啟動3.5 配置管理 四、注意事項和進階使用4.1 注意事項4.2 進階使用 背景&#xff1a; 我們在學習和工作中&#xff0c;經常會遇到各種需要自己搭建環境的場景&#xff0c;這…

java中的線程安全的集合

1.ConcurrentHashMap。 key,value結構。 jdk1.7通過分段鎖保證不同段同時操作是線程安全的&#xff0c;但并發不足&#xff0c;jdk1.8通過node節點鎖和CAS保證并發安全。不同node節點可以并發讀寫。通過它的computer,computerIfAbsent,等可以保證原子更新value。ifAbsent表示有…

MySQL問題:MySQL中使用索引一定有效嗎?如何排查索引效果

不一定有效&#xff0c;當查詢條件中不包含索引列或查詢條件復雜且不匹配索引順序 對于一些小表&#xff0c;MySQL可能選擇全表掃描而非使用索引&#xff0c;因為全表掃描的開銷可能更小 最終是否用上索引是根據MySQL成本計算決定的&#xff0c;評估CPU和I/O成本 排查索引效…

使用vscode MSVC CMake進行C++開發和Debug

使用vscode MSVC CMake進行C開發和Debug 前言軟件安裝安裝插件構建debuug方案一debug方案二其他 前言 一般情況下我都是使用visual studio來進行c開發的&#xff0c;但是由于python用的是vscode&#xff0c;所以二者如果統一的話能稍微提高一點效率。 軟件安裝 需要安裝的軟…

【后端高階面經:消息隊列篇】29、Kafka高性能探秘:零拷貝、順序寫與分區并發實戰

一、 順序寫入:磁盤性能的極致挖掘 Kafka的高性能本質上源于對磁盤順序訪問的深度優化。 傳統隨機寫入的磁盤操作需要磁頭頻繁尋道,機械硬盤的隨機寫性能通常僅為100IOPS左右,而Kafka通過追加日志(Append-Only Log)模式,將所有消息按順序寫入分區文件,使磁盤操作轉化為…

AI預測3D新模型百十個定位預測+膽碼預測+去和尾2025年5月27日第90彈

從今天開始&#xff0c;咱們還是暫時基于舊的模型進行預測&#xff0c;好了&#xff0c;廢話不多說&#xff0c;按照老辦法&#xff0c;重點8-9碼定位&#xff0c;配合三膽下1或下2&#xff0c;殺1-2個和尾&#xff0c;再殺6-8個和值&#xff0c;可以做到100-300注左右。 (1)定…

Git 初次推送遠程倉庫

Git 初次推送遠程倉庫&#xff08;完整實戰版&#xff09; —— 涵蓋重命名分支、強制合并、沖突解決等高頻場景 &#x1f525; 核心流程圖 初始化 → 關聯遠程 → 提交代碼 → 處理分支沖突 → 成功推送 1. 基礎操作&#xff08;全新倉庫&#xff09; # 初始化 cd /your/pr…

Pluto實驗報告——基于FM的音頻信號傳輸并解調恢復

目錄 一、實驗目的 ................................ ................................ ................................ .................. 3 二、實驗內容 ................................ ................................ ................................ ......…

輸出數據OutputFormat案例

輸出數據OutputFormat 案例&#xff1a; www.atguigu.com www.atguigu.com www.atguigu.com www.hao123.com www.shouhu.com www.baidu.com www.atguigu.com www.qq.com www.gaga.com www.qinghua.com www.sogou.com www.baidu.com www.alibaba.com …

STM32與ESP32的區別

STM32與ESP32都是當前電子行業中廣泛使用的微控制器芯片&#xff0c;但二者在架構、功能、應用領域以及開發生態上均存在顯著差異。需要高度實時響應和低功耗的系統通常適合STM32&#xff0c;而需要網絡連接和便捷無線通訊的物聯網應用通常更適合ESP32。 一、架構與性能 STM32…

YOLOv11改進 | Neck篇 | 雙向特征金字塔網絡BiFPN助力YOLOv11有效漲點

YOLOv11改進 | Neck篇 | 雙向特征金字塔網絡BiFPN助力YOLOv11有效漲點 引言 目標檢測領域的最新進展表明,特征金字塔網絡(FPN)的設計對模型性能具有決定性影響。本文詳細介紹如何將**雙向特征金字塔網絡(BiFPN)**集成到YOLOv11的Neck部分,通過改進的多尺度特征融合機制…

Python后端框架新星Robyn:性能與開發體驗的雙重革命

引言:Python后端框架的進化之路 在Web開發領域,Python生態長期被Flask、Django等經典框架主導。隨著異步編程需求的增長和高并發場景的普及,開發者對框架性能提出了更高要求。2023年,一款名為Robyn的新型Web框架橫空出世,以其獨特的Rust底層架構和優雅的Python API設計,…

ADS學習筆記(三) 瞬態仿真

參考書籍:見資源綁定,書籍3.4 瞬態仿真,書籍鏈接: https://pan.baidu.com/s/1pjw8p7r3-1x8qcC1-hljFQ?pwd4v79 提取碼: 4v79 本文為對實驗內容的補充 瞬態仿真放大倍數與交流仿真不一致 為什么對同一個BJT電路進行交流信號仿真和進行瞬態仿真,得出交流信號仿真的放大倍數是3.…

Flask 會話管理:從原理到實戰,深度解析 session 機制

1、Flask中session 的實現原理&#xff1a;服務器與客戶端的協作 HTTP 協議是無狀態的——服務器無法區分兩次請求是否來自同一用戶。這意味著&#xff0c;用戶登錄后跳轉到其他頁面時&#xff0c;服務器會“忘記”用戶身份。 為解決這一問題&#xff0c;Web 開發中引入了會話…

學習STC51單片機16(芯片為STC89C52RCRC)

每日一言 那些讓你喘不過氣的日子&#xff0c;正是蛻變的開始。 串口編程寄存器分析&#xff08;紅色框里面的這個是串口助手里面生成的波特率初始化函數哈&#xff09; 我們就根據以上的寄存器分析&#xff0c;因為這個是配置波特率的需要的寄存器 PCON smod 0 就是PCON的bit…

crud方法命名示例

以下是基于表名dste_project_indicator&#xff08;項目指標表&#xff09;的完整命名示例&#xff0c;覆蓋各類增刪改查場景&#xff1a; 1. 表名與實體類映射 // 表名&#xff1a;dste_project_indicator // 實體類&#xff1a;DsteProjectIndicatorEntity public class Ds…

AI時代新詞-人工智能生成內容(AIGC)

一、什么是人工智能生成內容&#xff08;AIGC&#xff09;&#xff1f; 人工智能生成內容&#xff08;Artificial Intelligence Generated Content&#xff0c;簡稱AIGC&#xff09;是指利用人工智能技術生成的各種形式的內容&#xff0c;包括文字、圖像、音頻和視頻等。AIGC的…