算法沉淀——動態規劃之兩個數組的 dp(上)(leetcode真題剖析)

在這里插入圖片描述

算法沉淀——動態規劃之兩個數組的 dp

  • 01.最長公共子序列
  • 02.不相交的線
  • 03.不同的子序列
  • 04.通配符匹配

01.最長公共子序列

題目鏈接:https://leetcode.cn/problems/longest-common-subsequence/

給定兩個字符串 text1text2,返回這兩個字符串的最長 公共子序列 的長度。如果不存在 公共子序列 ,返回 0

一個字符串的 子序列 是指這樣一個新的字符串:它是由原字符串在不改變字符的相對順序的情況下刪除某些字符(也可以不刪除任何字符)后組成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

兩個字符串的 公共子序列 是這兩個字符串所共同擁有的子序列。

示例 1:

輸入:text1 = "abcde", text2 = "ace" 
輸出:3  
解釋:最長公共子序列是 "ace" ,它的長度為 3 。

示例 2:

輸入:text1 = "abc", text2 = "abc"
輸出:3
解釋:最長公共子序列是 "abc" ,它的長度為 3 。

示例 3:

輸入:text1 = "abc", text2 = "def"
輸出:0
解釋:兩個字符串沒有公共子序列,返回 0 。 

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1text2 僅由小寫英文字符組成。

思路

狀態轉移方程:

  1. 如果當前字符相同(text1[i] == text2[j]),說明我們找到了一個新的公共字符,因此最長公共子序列的長度在之前的基礎上加 1:
    • dp[i][j] = dp[i - 1][j - 1] + 1
  2. 如果當前字符不同(text1[i] != text2[j]),則我們需要考慮去掉 text1[i] 或者去掉 text2[j] 之后的最長公共子序列。這可以通過比較 text1 的前 i-1 個字符和 text2 的前 j 個字符的最長公共子序列長度,以及 text1 的前 i 個字符和 text2 的前 j-1 個字符的最長公共子序列長度,取最大值:
    • dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

初始化:為了方便處理邊界情況,我們在文本 text1text2 前各添加一個空字符,即 text1[0]text2[0] 表示空串。這樣,dp 表的大小為 (m+1) x (n+1),其中 mn 分別是兩個文本的長度。初始化時,將第一行和第一列的值都設置為 0。

填表順序:最終的結果存儲在 dp[m][n] 中,其中 mn 是兩個文本的長度。這個值表示 text1text2 的最長公共子序列的長度。

代碼

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int m=text1.size(),n=text2.size();text1=" "+text1,text2=" "+text2;vector<vector<int>> dp(m+1,vector<int>(n+1));for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)if(text1[i]==text2[j]) dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);return dp[m][n];}
};

02.不相交的線

題目鏈接:https://leetcode.cn/problems/uncrossed-lines/

在兩條獨立的水平線上按給定的順序寫下 nums1nums2 中的整數。

現在,可以繪制一些連接兩個數字 nums1[i]nums2[j] 的直線,這些直線需要同時滿足滿足:

  • nums1[i] == nums2[j]
  • 且繪制的直線不與任何其他連線(非水平線)相交。

請注意,連線即使在端點也不能相交:每個數字只能屬于一條連線。

以這種方法繪制線條,并返回可以繪制的最大連線數。

示例 1:

輸入:nums1 = [1,4,2], nums2 = [1,2,4]
輸出:2
解釋:可以畫出兩條不交叉的線,如上圖所示。 
但無法畫出第三條不相交的直線,因為從 nums1[1]=4 到 nums2[2]=4 的直線將與從 nums1[2]=2 到 nums2[1]=2 的直線相交。

示例 2:

輸入:nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2]
輸出:3

示例 3:

輸入:nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1]
輸出:2 

提示:

  • 1 <= nums1.length, nums2.length <= 500
  • 1 <= nums1[i], nums2[j] <= 2000

思路

如果我們希望確保兩條直線不相交,我們可以將問題轉化為在兩個整數數組中尋找最長的公共子序列。這是因為,如果兩條直線不相交,它們在同一列上對應的兩個元素在兩個數組中的順序關系應該保持一致。

這個問題可以用動態規劃來解決,具體步驟如下:

  1. 狀態表達
    • 我們選取兩個數組分別表示兩條直線,數組長度分別為 mn
    • 定義狀態表 dp,其中 dp[i][j] 表示數組1的前 i 個元素和數組2的前 j 個元素中的最長公共子序列的長度。
  2. 狀態轉移方程
    • 如果 nums1[i] == nums2[j],說明找到了一個公共元素,狀態轉移方程為 dp[i][j] = dp[i-1][j-1] + 1
    • 如果 nums1[i] != nums2[j],則需要在兩個數組的前面部分分別尋找最長公共子序列,狀態轉移方程為 dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  3. 初始化
    • 為了方便處理邊界情況,可以在兩個數組的前面各添加一個虛擬元素,表示空串。
    • 初始化 dp 數組的第一行和第一列為0,因為空串與任何數組的最長公共子序列長度都為0。
  4. 填表順序
    • 從上到下,從左到右填寫 dp 數組。
  5. 返回值
    • 最終的結果存儲在 dp[m][n] 中,表示兩個數組的最長公共子序列的長度。

代碼

class Solution {
public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {int m=nums1.size(),n=nums2.size();vector<vector<int>> dp(m+1,vector<int>(n+1));for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)if(nums1[i-1]==nums2[j-1]) dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);return dp[m][n];}   
};

03.不同的子序列

題目鏈接:https://leetcode.cn/problems/distinct-subsequences/

你兩個字符串 st ,統計并返回在 s子序列t 出現的個數,結果需要對 109 + 7 取模。

示例 1:

輸入:s = "rabbbit", t = "rabbit"
輸出:3
解釋:
如下所示, 有 3 種可以從 s 中得到 "rabbit" 的方案。
rabbbit
rabbbit
rabbbit

示例 2:

輸入:s = "babgbag", t = "bag"
輸出:5
解釋:
如下所示, 有 5 種可以從 s 中得到 "bag" 的方案。 
babgbag
babgbag
babgbag
babgbag
babgbag

提示:

  • 1 <= s.length, t.length <= 1000
  • st 由英文字母組成

思路

  1. 狀態表達

    • 選取第一個字符串 [0, i] 區間以及第二個字符串 [0, j] 區間作為研究對象,結合題目要求來定義狀態表達。
    • 在這道題中,狀態表達為 dp[i][j],表示在字符串 s[0, j] 區間內的所有子序列中,有多少個 t 字符串 [0, i] 區間內的子串。
  2. 狀態轉移方程

    • 根據最后一個位置的元素,結合題目要求進行分類討論:

      • t[i] == s[j]
        

        時:

        • 選擇 s[j] 作為結尾,相當于在狀態 dp[i-1][j-1] 中的所有符合要求的子序列的后面,再加上一個字符 s[j],此時 dp[i][j] = dp[i-1][j-1]
        • 不選擇 s[j] 作為結尾,相當于選擇了狀態 dp[i][j-1] 中所有符合要求的子序列,繼承了上個狀態里面的求得的子序列,此時 dp[i][j] = dp[i][j-1]
      • t[i] != s[j]
        

        時:

        • 子序列只能從 dp[i][j-1] 中選擇所有符合要求的子序列,繼承上個狀態里面求得的子序列,此時 dp[i][j] = dp[i][j-1]
    • 綜上,狀態轉移方程為:

      • 所有情況下都可以繼承上一次的結果:dp[i][j] = dp[i][j-1]
      • t[i] == s[j] 時,可以多選擇一種情況:dp[i][j] += dp[i-1][j-1]
  3. 初始化

    • 引入空串后,方便初始化,注意下標的映射關系以及確保后續填表是正確的。
    • s 為空時,t 的子串中有一個空串和它一樣,因此初始化第一行全部為 1
  4. 填表順序

    • 從上往下填每一行,每一行從左往右。
  5. 返回值

    • 根據狀態表達,返回 dp[m][n] 的值。

代碼

class Solution {
public:int numDistinct(string s, string t) {int m=t.size(),n=s.size();vector<vector<double>> dp(m+1,vector<double>(n+1));for(int j=0;j<=n;j++) dp[0][j]=1;for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){dp[i][j]+=dp[i][j-1];if(t[i-1]==s[j-1]) dp[i][j]+=dp[i-1][j-1];}return dp[m][n];}
};

04.通配符匹配

題目鏈接:https://leetcode.cn/problems/wildcard-matching/

給你一個輸入字符串 (s) 和一個字符模式 (p) ,請你實現一個支持 '?''*' 匹配規則的通配符匹配:

  • '?' 可以匹配任何單個字符。
  • '*' 可以匹配任意字符序列(包括空字符序列)。

判定匹配成功的充要條件是:字符模式必須能夠 完全匹配 輸入字符串(而不是部分匹配)。

示例 1:

輸入:s = "aa", p = "a"
輸出:false
解釋:"a" 無法匹配 "aa" 整個字符串。

示例 2:

輸入:s = "aa", p = "*"
輸出:true
解釋:'*' 可以匹配任意字符串。

示例 3:

輸入:s = "cb", p = "?a"
輸出:false
解釋:'?' 可以匹配 'c', 但第二個 'a' 無法匹配 'b'。

提示:

  • 0 <= s.length, p.length <= 2000
  • s 僅由小寫英文字母組成
  • p 僅由小寫英文字母、'?''*' 組成

思路

在處理兩個字符串之間的動態規劃問題時,通常按照以下步驟進行:

  1. 狀態表達

    • 選取第一個字符串 [0, i] 區間以及第二個字符串 [0, j] 區間作為研究對象,結合題目的要求定義狀態表達。
    • 在這道題中,我們定義狀態表達為 dp[i][j],表示字符串 p[0, j] 區間內的子串能否匹配字符串 s[0, i] 區間內的子串。
  2. 狀態轉移方程

    • 根據最后一個位置的元素,結合題目要求,進行分類討論:
      • s[i] == p[j]p[j] == '?' 時,兩個字符串匹配上了當前的一個字符,只能從 dp[i-1][j-1] 中看當前字符前面的兩個子串是否匹配,繼承上個狀態中的匹配結果,dp[i][j] = dp[i][j-1]
      • p[j] == '*' 時,匹配策略有兩種選擇:
        • 選擇 '*' 匹配空字符串,相當于它匹配了一個寂寞,直接繼承狀態 dp[i][j-1]dp[i][j] = dp[i][j-1]
        • 選擇 '*' 向前匹配 1 ~ n 個字符,直至匹配整個 s 串。相當于從 dp[k][j-1] (0 <= k <= i) 中所有匹配情況中,選擇性繼承可以成功的情況,dp[i][j] = dp[k][j-1] (0 <= k <= i)。
      • p[j] 不是特殊字符且不與 s[i] 相等時,無法匹配。綜上,狀態轉移方程為:
        • s[i] == p[j]p[j] == '?' 時:dp[i][j] = dp[i-1][j-1]
        • p[j] == '*' 時,狀態轉移方程優化為:dp[i][j] = dp[i-1][j] || dp[i][j-1]
  3. 初始化

    • dp 數組的值表示是否匹配,初始化整個數組為 false
    • 由于需要用到前一行和前一列的狀態,初始化第一行和第一列。
      • dp[0][0] 表示兩個空串是否匹配,初始化為 true
      • 第一行表示 s 為空串,p 串與空串只有一種匹配可能,即 p 串表示為 "***",此時也相當于空串匹配上空串,將所有前導為 "*"p 子串與空串的 dp 值設為 true
      • 第一列表示 p 為空串,不可能匹配上 s 串,跟隨數組初始化即可。
  4. 填表順序

    • 從上往下填每一行,每一行從左往右。
  5. 返回值

    • 根據狀態表達,返回 dp[m][n] 的值。

代碼

class Solution {
public:bool isMatch(string s, string p) {int m=s.size(),n=p.size();s=" "+s,p=" "+p;vector<vector<bool>> dp(m+1,vector<bool>(n+1));dp[0][0]=true;for(int i=1;i<=n;i++)if(p[i]=='*') dp[0][i]=true;else break;for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){if(p[j]=='*') dp[i][j]=dp[i][j-1]||dp[i-1][j];else dp[i][j]=(p[j]=='?'||s[i]==p[j])&&dp[i-1][j-1];}return dp[m][n];}
};

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

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

相關文章

0201sherlock(福爾摩斯)-通過名稱尋找媒體賬號(地址)-github-開源項目學習

文章目錄 一 項目簡介二 項目安裝和演示1 安裝2 演示 三 源碼分析1 項目結構2 主程序源代碼分析 四 添加自定義網址結語 一 項目簡介 二 項目安裝和演示 1 安裝 # clone the repo $ git clone https://github.com/sherlock-project/sherlock.git# change the working direct…

前端訪問線上的html 怎么給html 的js中傳遞 訪問路徑

1.需求 我想在項目中訪問一個html 文件 這個html 文件中 跳轉的又是另一個地址 。這個需求是為了讓實現公眾號H5 的重定向登錄 主要是 一個oem 系統 他有多個公眾號 但是每個公眾號 都有對應不用的域名 2.動態域名 <!DOCTYPE html> <html lang"zh">&…

opencv--使用直方圖找谷底進行確定分割閾值

直方圖原理就不說了&#xff0c;大家自行百度 直方圖可以幫助分析圖像中的灰度變化&#xff0c;進而幫助確定最優二值化的灰度閾值&#xff08;threshold level&#xff09;。如果物體與背景的灰度值對比明顯&#xff0c;此時灰度直方圖就會包含雙峰&#xff08;bimodal histo…

Python web框架fastapi數據庫操作ORM(一)

文章目錄 Fastapi ORM操作1、創建模型2、創建數據庫連接配置文件3、啟動項目4、根據模型類創建數據庫表1. 初始化配置&#xff0c;只需要使用一次2. 初始化數據庫&#xff0c;一般情況下只用一次3. 更新模型并進行遷移4. 重新執行遷移&#xff0c;寫入數據庫5. 回到上一個版本6…

Oracle 11g升級19c 后部分查詢功能很慢

*Oracle 11g升級19c 后部分查詢功能很慢 今天生產突然有個查詢非常慢&#xff0c;日志顯示執行了50秒左右&#xff0c;但是從日志中拿出SQL在PLSQL執行&#xff0c;發現用時不到1秒&#xff0c;查看SQL,懷疑是下面幾種原因導致 1、使用函數不當 UNIT.UNIT_CODE LIKE CONCAT(‘…

狀態碼轉文字!!!(表格數字轉文字)

1、應用場景&#xff1a;在我們的數據庫表中經常會有status這個字段&#xff0c;這個字段經常表示此類商品的狀態&#xff0c;例如&#xff1a;0->刪除&#xff0c;1->上架&#xff0c;0->下架&#xff0c;等等。 2、我們返回給前端數據時&#xff0c;如果在頁面顯示0…

python 線程、進程區別與事例

線程&#xff1a;簡單來說&#xff0c;一個進程中包含多個線程&#xff0c;比如打開一個 QQ&#xff08;進程&#xff09;&#xff0c;然后你一邊聊 QQ&#xff08;一個線程&#xff09;&#xff0c;一邊用 QQ 傳送文件&#xff08;一個線程&#xff09;&#xff0c;等等。在一…

Linux中如何執行腳本

要執行一個保存在文件中的腳本&#xff0c;可以按照以下步驟進行&#xff1a; 1. 創建腳本文件&#xff1a; 首先&#xff0c;使用文本編輯器&#xff08;如 ?vi?、?nano?等&#xff09;創建一個新的腳本文件&#xff0c;并將需要執行的命令寫入到文件中。例如&#xff0…

【Unity】在Unity中導出WebGL并讀取Excel數據的實現方法

在游戲開發中&#xff0c;數據的處理和導出是至關重要的環節之一。Unity作為一款強大的游戲開發引擎&#xff0c;提供了豐富的工具和功能來處理和導出數據&#xff0c;包括將游戲導出為WebGL應用&#xff0c;并讀取外部數據文件&#xff0c;比如Excel表格。本文將介紹如何在Uni…

gpt生成器,批量gpt文章生成器

GPT&#xff08;生成式預訓練模型&#xff09;生成器軟件在當今的數字化時代扮演著越來越重要的角色&#xff0c;它們通過人工智能技術&#xff0c;可以自動生成各種類型的文章內容&#xff0c;為用戶提供了無限的創作可能性。本文將介紹6款不同的GPT生成器軟件&#xff0c;并介…

STM32自學?AD單通道

程序的最終運行成果: 當轉動電位器時&#xff0c;數值和電壓值發生變化 ad.c文件 #include "stm32f10x.h" #include "stm32f10x_adc.h" #include "ad.h" #include "stdint.h" void ad_Init(void) { /* 初始化步驟&#xff1a;…

java學習筆記-初級

一、變量 1.雙標簽 <!-- 外部js script 雙標簽 --><script srcmy.js></script> 在新文件my.js里面寫&#xff1a; 2.字符串定義&#xff1a; //外單內雙var str 我是一個"高富帥"的程序員;console.log(str);// 字符串轉義字符 都是用 \ 開頭 …

并發編程中常見的設計模式,c++多線程如何設計

C多線程設計&#xff08;任務的“多對一”、“一對多”、“多對多”情況 該如何設計線程&#xff1f;&#xff09; C書籍中并未找到對多線程設計&#xff0c;有很完整詳細的總結&#xff01;&#xff01;C并發編程書籍中也只是一些理論或則零散的多線程實例。無奈&#xff0c;…

MySQL-MHA搭建、故障測試

一、架構說明 MHA&#xff08;Master High Availability&#xff09;是一個用于 MySQL 主從復制管理和自動故障轉移的開源工具集。MHA 的主要目的是提供 MySQL 環境的高可用性和自動故障轉移功能&#xff0c;確保在主庫發生故障時能夠快速切換到備庫&#xff0c;降低業務中斷時…

ElasticSearch之Completion Suggester

寫在前面 通過completion suggester可以實現如下的效果&#xff1a; 其實就是做的like xxx%這種。通過FST這種數據結構來存儲&#xff0c;實現快速的前綴匹配&#xff0c;并且可以將es所有的數據加載到內存中所以速度completion的查詢速度非常快。 需要注意&#xff0c;如果…

JWT令牌的使用教程

一、導入maven依賴 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> </dependency> 二、導入JWT工具類 &#xff08;工具類&#xff09; import io.jsonwebtoken.Cl…

HUAT——Fasc——算法組學習筆記

目錄 系列文章目錄 前言 一、配置相關環境 二、創建工作空間 1.創建工作空間并初始化 2.進入 src 創建 ros 包并添加依賴 三、HelloWorld(C版) 1.進入 ros 包的 src 目錄編輯源文件 2.編輯 ros 包下的 Cmakelist.txt文件 3.進入工作空間目錄并編譯 四 運行程序 五 …

docker 基礎(二)

常見命令 Docker最常見的命令就是操作鏡像、容器的命令&#xff0c;詳見官方文檔&#xff1a;https://docs.docker.com/ 數據卷 命令說明文檔地址docker volume create創建數據卷docker volume createdocker volume ls創建數據卷docker volume lsdocker volume rm查看所有數…

asp.net core webapi接收application/x-www-form-urlencoded和form-data參數

框架&#xff1a;asp.net core webapiasp.net core webapi接收參數&#xff0c;請求變量設置 目錄 接收multipart/form-data、application/x-www-form-urlencoded類型參數接收URL參數接收上傳的文件webapi接收json參數 接收multipart/form-data、application/x-www-form-urlenc…

Swiper實現輪播效果

swiper官網&#xff1a;https://3.swiper.com.cn/ <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title&…