「動態規劃」線性DP:最長上升子序列(LIS)|編輯距離 / LeetCode 300|72(C++)

概述

DP,即動態規劃是解決最優化問題的一類算法,我們要通過將原始問題分解成規模更小的、相似的子問題,通過求解這些易求解的子問題來計算原始問題。

線性DP是一類基本DP,我們來通過它感受DP算法的奧義。


最長上升子序列(LIS)

LIS問題,即最長上升子序列問題,是指找到原始序列中最長的單調元素序列這些(這些元素在原始序列中不一定是挨在一起的)。

LeetCode 2521:

給你一個整數數組?nums?,找到其中最長嚴格遞增子序列的長度。

子序列?是由數組派生而來的序列,刪除(或不刪除)數組中的元素而不改變其余元素的順序。例如,[3,6,2,7]?是數組?[0,3,1,6,2,2,7]?的子序列。

示例 1:

輸入:nums = [10,9,2,5,3,7,101,18]
輸出:4
解釋:最長遞增子序列是 [2,3,7,101],因此長度為 4 。

示例 2:

輸入:nums = [0,1,0,3,2,3]
輸出:4

思路

我們來找到規模更小的子問題。

對于本題來說,什么是規模更小的子問題?

如果在故事的最后我們希望找到整個數組的LIS,不妨先找到整個數組中更小規模的上升序列

如果我們用稱可能的答案為一個狀態,那么我們需要兩個要素來表示這個狀態,一個是描述規模的范圍,一個描述這一段上升序列的長度。

那我們不妨定義?int dp[N],其中dp[i] = num,表示從原始數組中以nums[i]結尾的LIS為num。

當我們未開始計算時,dp[i] = 1,因為至少nums[i]這一個元素算是一個長度為1的遞增子序列。

具體的,

const int n = nums.size();    // 獲取數組的長度
vector<int> dp(n, 1);         // 考慮到n是一個運行期變量,我們使用vector

現在,我們來求解dp[i] = ? 的子問題。


算法過程

每個子問題都要被求解,前提是比它小的問題被求解了,而不論大小規模,這些問題的求解過程是一樣的。

?求解DP問題,這被稱為狀態轉移,也就是我們從小規模的狀態推導出大規模的狀態。

對于下標i來說, dp[i] = dp[j] + 1,其中 j < i && nums[j] <?nums[i],考慮到可能有多個j符合情況,我們取最大值。

從0計算到n - 1,當計算 i 時,j 一定計算過了,所以這是合理的。

直觀來說,我們需要一個二重循環:

for (int i = 0; i < n; i++) {for (int j = 0; j < i; j++)if (nums[j] < nums[i])dp[i] = max(dp[i], dp[j] + 1);}

Code

class Solution {
public:int lengthOfLIS(vector<int>& nums) {const int n = nums.size();vector<int> dp(n, 1);int ans = INT_MIN;for (int i = 0; i < n; i++) {for (int j = 0; j < i; j++)if (nums[j] < nums[i])dp[i] = max(dp[i], dp[j] + 1);ans = max(ans, dp[i]);}return ans;}
};

這種枚舉是O(n2),怎么優化呢??

優化方案

還記得二分查找嗎?

「數組」二分查找模版|二段性分析|恢復二段性 / LeetCode 35|33|81(C++)

有兩件事:

  • LIS一定是單調的。
  • 子問題LIS的起點越小、差分(LIS[i] - LIS[i - 1])越小,即上升越緩,越有利于后續的元素追加到末尾。

這兩件事很直觀,幾乎無須證明。

在遍歷到i時,我們不妨記錄當前LIS,然后這么做:

  • 若nums[i]可以追加,則直接追加。
  • 若nums[i]不可以追加,則二分找到LIS中當前大于nums[i]的元素,如果這個元素的上一個元素小于nums[i],則用nums[i]替換這個元素,這就使得LIS的差分減小。

二分是由LIS的單調性保證的。

Code

class Solution {
public:int lengthOfLIS(vector<int>& nums) {const int n = nums.size();vector<int> LIS(n);int len = 0;for (int i = 0; i < n; i++)if (!len || nums[i] > LIS[len - 1])LIS[len++] = nums[i];else {auto pos = upper_bound(LIS.begin(), LIS.begin() + len, nums[i]);if(pos == LIS.begin() || *prev(pos) < nums[i])*pos = nums[i];}return len;}
};  

復雜度

時間復雜度:?O(nlogn)?//需求解n個狀態,每次求解通過二分進行。

空間復雜度:?O(n) ????????//預留dp數組空間


編輯距離

編輯距離,也稱為Levenshtein Distance,是衡量兩個字符串差異的一種度量方法。它定義為將一個字符串轉換成另一個字符串所需的最少編輯操作次數,包括插入、刪除和替換字符。編輯距離越小,兩個字符串越相似。

LeetCode 72:

給你兩個單詞?word1?和?word2,?請返回將?word1?轉換成?word2?所使用的最少操作數??。

你可以對一個單詞進行如下三種操作:

  • 插入一個字符
  • 刪除一個字符
  • 替換一個字符

示例?1:

輸入:word1 = "horse", word2 = "ros"
輸出:3
解釋:
horse -> rorse (將 'h' 替換為 'r')
rorse -> rose (刪除 'r')
rose -> ros (刪除 'e')

示例?2:

輸入:word1 = "intention", word2 = "execution"
輸出:5
解釋:
intention -> inention (刪除 't')
inention -> enention (將 'i' 替換為 'e')
enention -> exention (將 'n' 替換為 'x')
exention -> exection (將 'n' 替換為 'c')
exection -> execution (插入 'u')

思路

依舊要將原始問題處理成規模更小的子問題。

上一題倒是很爽,要存儲兩個信息,于是就用下標和值處理了,但這次我們需要三個信息,就需要二維數組了。

那我們不妨定義 `int dp[N][M]`,其中dp[i][j] = num,

表示從數組1中[1,? i]子數組與數組2中[1,? j]子數組的編輯距離為num。

做如下初始化:

for (int i = 1; i <= n; i++)dp[i][0] = i;
for (int j = 1; j <= m; j++)dp[0][j] = j;

*注意*:dp[i]對應原始的word[i - 1],我們從i = 1開始循環,即做了一格偏移,這是為了處理邊界,直觀來講,可以認為dp[i]代表原始數組的前i個字符。?

解題過程

我們能想到很直觀的二維循環。

有兩種可能:

  • word[i] == word[j],這是好事啊,編輯距離無需增加,dp[i][j] = dp[i - 1][j - 1];
  • word[i] != word[j],dp[i][j] 至少為 dp[i - 1][j - 1],然后考慮:刪除/替換/插入,這三者不過都是讓二者相等的手段,我們只需要考慮之前的事,然后+1就行了。
  1. 考慮刪掉word1[i - 1]/在word2[j?- 1]后插入word1[i?- 1],則dp[i][j] = dp[i - 1][j] + 1;
  2. 考慮刪掉word2[j - 1]/在word1[i - 1]后插入word2[j - 1],則dp[i][j] = dp[i][j - 1] + 1;
  3. 考慮將word1[i - 1]替換為word2[j - 1],則dp[i][j] = dp[i - 1][j - 1] + 1;

注意這里dp[i]對應word[i - 1]。

以上三者,取最大值:

for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) {dp[i][j] = dp[i - 1][j - 1];if (word1[i - 1] != word2[j - 1])dp[i][j] = min({dp[i][j], dp[i - 1][j], dp[i][j - 1]}) + 1;}

計算 i 和 j 時,i - 1 和 j - 1一定計算過了,這是合法的。

Code

class Solution {
public:int minDistance(string word1, string word2) {const int n = word1.size(), m = word2.size();vector dp(n + 1, vector(m + 1, 0));for (int i = 1; i <= n; i++)dp[i][0] = i;for (int j = 1; j <= m; j++)dp[0][j] = j;for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) {dp[i][j] = dp[i - 1][j - 1];if (word1[i - 1] != word2[j - 1])dp[i][j] = min({dp[i][j], dp[i - 1][j], dp[i][j - 1]}) + 1;}return dp.back().back();}
};

復雜度

時間復雜度:?O(nm)?//需求解n*m個狀態。

空間復雜度:?O(nm) //預留n*m個空間。


總結

線性dp的狀態轉移依靠的是非常直觀的線性增長的問題規模。

dp算法都是通過求解子問題進而求解原始問題的一類算法,希望你了解線性dp這類基本dp后有所體會。

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

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

相關文章

【NumPy完全指南】從基礎操作到高性能計算實戰

&#x1f4d1; 目錄 一、NumPy核心價值1.1 科學計算現狀分析1.2 ndarray設計哲學 二、核心數據結構解析2.1 ndarray內存布局2.2 數據類型體系 三、矢量化編程實踐3.1 通用函數(ufunc)示例3.2 廣播機制圖解 四、高性能計算進階4.1 內存預分配策略4.2 Cython混合編程 五、典型應用…

你的項目有‘哇‘點嗎?

你的項目有哇點嗎&#xff1f; 刷了一下午招聘軟件&#xff0c;發現沒&#xff1f;大廠JD里總愛寫有創新力者優先——可你們的簡歷&#xff0c;創新力還不如食堂菜單&#xff01; 程序員寫項目最大的誤區&#xff1a;把創新當彩蛋藏最后&#xff01;什么參與需求評審負責模塊…

2025年危化品安全員考試題庫及答案

一、單選題 126.安全生產監督管理部門和負有安全生產監督管理職責的有關部門逐級上報事故情況,每級上報的時間不得超過&#xff08;&#xff09;小時。 A.2 B.6 C.12 答案&#xff1a;A 127.按照《安全生產法》規定,危險化學品生產經營單位的從業人員不服從管理,違反安全生…

第十六屆藍橋杯 C/C++ B組 題解

做之前的真題就可以發現&#xff0c;藍橋杯特別喜歡出找規律的題&#xff0c;但是我還是低估了官方的執念。本博客用于記錄第一次藍橋的過程&#xff0c;代碼寫的很爛&#xff0c;洛谷已經有的題解&#xff0c;這里不再贅述&#xff0c;只說自己遇到的問題。用于以后回顧和查找…

C++ 基于多設計模式下的同步異步?志系統-2項目實現

?志系統框架設計 1.?志等級模塊:對輸出?志的等級進?劃分&#xff0c;以便于控制?志的輸出&#xff0c;并提供等級枚舉轉字符串功能。 ? OFF&#xff1a;關閉 ? DEBUG&#xff1a;調試&#xff0c;調試時的關鍵信息輸出。 ? INFO&#xff1a;提?&#xff0c;普通的提?…

提示詞工程(GOT)把思維鏈推理過程圖結構化

Graph of Thoughts&#xff08;GOT&#xff09;&#xff1f; 思維圖&#xff08;Graph of Thoughts&#xff09;是一種結構化的表示方法&#xff0c;用于描述和組織模型的推理過程。它將信息和思維過程以圖的形式表達&#xff0c;其中節點代表想法或信息&#xff0c;邊代表它們…

登錄github失敗---解決方案

登錄github失敗—解決方案 1.使用 Microsoft Edge 瀏覽器 2.https://www.itdog.cn/dns/ 查詢 github.global.ssl.fastly.net github.com 兩個 域名的 IP 3.修改DNS 為 8.8.8.8 8.8.4.4 4.修改windows hosts 文件 5. 使用 Microsoft Edge 瀏覽器 打開github.com

Spring AOP概念及其實現

一、什么是AOP 全稱Aspect Oriented Programming&#xff0c;即面向切面編程&#xff0c;AOP是Spring框架的第二大核心&#xff0c;第一大為IOC。什么是面向切面編程&#xff1f;切面就是指某一類特定的問題&#xff0c;所以AOP也可以稱為面向特定方法編程。例如對異常的統一處…

強化學習_Paper_2017_Curiosity-driven Exploration by Self-supervised Prediction

paper Link: ICM: Curiosity-driven Exploration by Self-supervised Prediction GITHUB Link: 官方: noreward-rl 1- 主要貢獻 對好奇心進行定義與建模 好奇心定義&#xff1a;next state的prediction error作為該state novelty 如果智能體真的“懂”一個state&#xff0c;那…

spring中的@Configuration注解詳解

一、概述與核心作用 Configuration是Spring框架中用于定義配置類的核心注解&#xff0c;旨在替代傳統的XML配置方式&#xff0c;通過Java代碼實現Bean的聲明、依賴管理及環境配置。其核心作用包括&#xff1a; 標識配置類&#xff1a;標記一個類為Spring的配置類&#xff0c;…

7.計算機網絡相關術語

7. 計算機網絡相關術語 ACK (Acknowledgement) 確認 ADSL (Asymmetric Digital Subscriber Line) 非對稱數字用戶線 AP (Access Point) 接入點 AP (Application) 應用程序 API (Application Programming Interface) 應用編程接口 APNIC (Asia Pacific Network Informatio…

Hadoop 集群基礎指令指南

目錄 &#x1f9e9; 一、Hadoop 基礎服務管理指令 ?? 啟動 Hadoop ?? 關閉 Hadoop &#x1f9fe; 查看進程是否正常運行 &#x1f4c1; 二、HDFS 常用文件系統指令 &#x1f6e0;? 三、MapReduce 作業運行指令 &#x1f4cb; 四、集群狀態監控指令 &#x1f4a1; …

【MySQL數據庫】事務

目錄 1&#xff0c;事務的詳細介紹 2&#xff0c;事務的屬性 3&#xff0c;事務常見的操作方式 1&#xff0c;事務的詳細介紹 在MySQL數據庫中&#xff0c;事務是指一組SQL語句作為一個指令去執行相應的操作&#xff0c;這些操作要么全部成功提交&#xff0c;對數據庫產生影…

一、OrcaSlicer源碼編譯

一、下載 1、OrcaSlicer 2.3.0版本的源碼 git clone https://github.com/SoftFever/OrcaSlicer.git -b v2.3.0 二、編譯 1、在OrcaSlicer目錄運行cmd窗口&#xff0c;輸入build_release.bat 2、如果出錯了&#xff0c;可以多運行幾次build_release.bat 3、在OrcaSlicer\b…

港口危貨儲存單位主要安全管理人員考試精選題目

港口危貨儲存單位主要安全管理人員考試精選題目 1、危險貨物儲存場所的電氣設備應符合&#xff08; &#xff09;要求。 A. 防火 B. 防爆 C. 防塵 D. 防潮 答案&#xff1a;B 解析&#xff1a;港口危貨儲存單位存在易燃易爆等危險貨物&#xff0c;電氣設備若不防爆&…

格雷希爾用于工業氣體充裝站的CZ系列氣罐充裝轉換連接器,其日常維護有哪些

格雷希爾氣瓶充裝連接器&#xff0c;長期用于壓縮氣體的快速充裝和壓縮氣瓶的氣密性檢測&#xff0c;需要進行定期的維護&#xff0c;為每一次的充裝提供更好的連接。下列建議的幾點維護準則適用于格雷希爾所有充注接頭&#xff0c;請非專業人士不要隨意拆卸連接器。 格雷希爾氣…

Java 多線程進階:什么是線程安全?

在多線程編程中&#xff0c;“線程安全”是一個非常重要但又常被誤解的概念。尤其對于剛接觸多線程的人來說&#xff0c;不理解線程安全的本質&#xff0c;容易寫出“偶爾出錯”的代碼——這類 bug 往往隱蔽且難以復現。 本文將用盡可能通俗的語言&#xff0c;從三個角度解釋線…

MSO-Player:基于vlc的Unity直播流播放器,支持主流RTSP、RTMP、HTTP等常見格式

MSO-Player 基于libVLC的Unity視頻播放解決方案 支持2D視頻和360度全景視頻播放的Unity插件 &#x1f4d1; 目錄 &#x1f3a5; MSO-Player &#x1f4cb; 功能概述&#x1f680; 快速入門&#x1f4da; 關鍵組件&#x1f4dd; 使用案例&#x1f50c; 依賴項&#x1f4cb; 注意…

navicat中導出數據表結構并在word更改為三線表(適用于navicat導不出doc)

SELECTCOLUMN_NAME 列名,COLUMN_TYPE 數據類型,DATA_TYPE 字段類型,IS_NULLABLE 是否為空,COLUMN_DEFAULT 默認值,COLUMN_COMMENT 備注 FROMINFORMATION_SCHEMA.COLUMNS WHEREtable_schema db_animal&#xff08;數據庫名&#xff09; AND table_name activity&#xff08;…

docker學習筆記6-安裝wordpress

一、創建自定義網絡、查看網絡 docker netword create blog docker network ls 二、 啟動mysql容器 啟動命令&#xff1a; docker run -d -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_DATABASEwordpress \ -v mysql-data:/var/lib/mysql \ -v /app/myconf:/etc…