動態規劃在子數組/子串問題

目錄

一、最大子數組和(LeetCode 53)

二、環形子數組的最大和(LeetCode 918)

三、乘積最大子數組(LeetCode 152)

四、乘積為正數的最長子數組長度(LeetCode 1567)

五、等差數列劃分(LeetCode 413)

六、最長湍流子數組(LeetCode 978)

七、單詞拆分(LeetCode 139)

?八、環繞字符串中唯一的子字符串(LeetCode 467)

總結


動態規劃(Dynamic Programming,簡稱 DP)是解決多階段決策問題的有效方法,尤其在處理子數組、子串相關問題時,能通過狀態定義和轉移,高效找到最優解。下面結合幾道經典題目,分享動態規劃在這類問題中的分析方法與思路。
?

一、最大子數組和(LeetCode 53)

?
問題描述
?
給定整數數組 ?nums?,找出和最大的連續子數組,返回其最大和。
?
分析思路
?
- 狀態定義:?dp[i]??表示以 ?nums[i]??結尾的連續子數組的最大和。
- 狀態轉移:對于 ?nums[i]?,有兩種選擇,要么加入前面的子數組(?dp[i - 1] + nums[i]?),要么自己作為新子數組的起點(?nums[i]?),取兩者最大值,即 ?dp[i] = max(nums[i], dp[i - 1] + nums[i])?。
- 初始條件:?dp[0] = nums[0]?,因為第一個元素自己就是一個子數組。
- 結果提取:遍歷 ?dp??數組,找到最大值。
?
代碼實現
?

class Solution {
public:int maxSubArray(vector<int>& nums) {int n = nums.size();vector<int> dp(n);dp[0] = nums[0];for (int i = 1; i < n; i++) {dp[i] = max(nums[i], dp[i - 1] + nums[i]);}int maxSum = INT_MIN;for (int num : dp) {maxSum = max(maxSum, num);}return maxSum;}
};

二、環形子數組的最大和(LeetCode 918)


?
問題描述
?
給定環形整數數組 ?nums?,返回非空子數組的最大可能和(環形意味著數組末端與開頭相連)。
?
分析思路
?
環形子數組的最大和有兩種情況:
?
- 情況一:最大和子數組在數組內部(非環形),可通過常規最大子數組和方法計算。
- 情況二:最大和子數組跨越數組首尾(環形),此時最大和等于數組總和減去內部最小子數組和。
需要注意,若數組所有元素都是負數,此時總和減去最小子數組和(也是負數)會得到錯誤結果,需單獨判斷。
?
代碼實現

class Solution {
public:int maxSubarraySumCircular(vector<int>& nums) {int n = nums.size();int sum = 0;vector<int> f(n + 1);auto g = f;f[0] = g[0] = 0;for (int i = 1; i <= n; i++) {sum += nums[i - 1];f[i] = max(nums[i - 1], f[i - 1] + nums[i - 1]);g[i] = min(nums[i - 1], g[i - 1] + nums[i - 1]);}int fmax = INT_MIN, gmin = INT_MAX;for (int i = 1; i <= n; i++) {fmax = max(fmax, f[i]);gmin = min(gmin, g[i]);}if (gmin == sum) return fmax;return max(fmax, sum - gmin);}
};

三、乘積最大子數組(LeetCode 152)


?
問題描述
?
給定整數數組 ?nums?,找出乘積最大的非空連續子數組,返回該乘積。
?
分析思路
?
由于乘法的特殊性,負數相乘可能得到正數,所以需要同時記錄以 ?nums[i]??結尾的子數組的最大乘積和最小乘積。
?
- 狀態定義:?f[i]??表示以 ?nums[i]??結尾的子數組的最大乘積,?g[i]??表示以 ?nums[i]??結尾的子數組的最小乘積。
- 狀態轉移:根據 ?nums[i]??的正負,選擇與 ?f[i - 1]??或 ?g[i - 1]??相乘,即:
- 若 ?nums[i] > 0?,?f[i] = max(nums[i], f[i - 1] * nums[i])?,?g[i] = min(nums[i], g[i - 1] * nums[i])?;
- 若 ?nums[i] < 0?,?f[i] = max(nums[i], g[i - 1] * nums[i])?,?g[i] = min(nums[i], f[i - 1] * nums[i])?;
- 若 ?nums[i] == 0?,?f[i] = g[i] = 0?。
- 初始條件:?f[0] = g[0] = nums[0]?。
- 結果提取:遍歷 ?f??數組,找到最大值。
?
代碼實現

class Solution {
public:int maxProduct(vector<int>& nums) {int n = nums.size();vector<int> f(n);f[0] = nums[0];vector<int> g(n);g[0] = nums[0];for (int i = 1; i < n; i++) {if (nums[i] > 0) {f[i] = max(nums[i], f[i - 1] * nums[i]);g[i] = min(nums[i], g[i - 1] * nums[i]);} else if (nums[i] < 0) {f[i] = max(nums[i], g[i - 1] * nums[i]);g[i] = min(nums[i], f[i - 1] * nums[i]);} else {f[i] = g[i] = 0;}}int maxProd = INT_MIN;for (int num : f) {maxProd = max(maxProd, num);}return maxProd;}
};

四、乘積為正數的最長子數組長度(LeetCode 1567)


?
問題描述
?
給定整數數組 ?nums?,求出乘積為正數的最長子數組的長度。
?
分析思路
?
- 狀態定義:?f[i]??表示以 ?nums[i - 1]??結尾的乘積為正數的最長子數組長度,?g[i]??表示以 ?nums[i - 1]??結尾的乘積為負數的最長子數組長度。
- 狀態轉移:根據 ?nums[i - 1]??的正負進行轉移:
- 若 ?nums[i - 1] > 0?,?f[i] = f[i - 1] + 1?,?g[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1?;
- 若 ?nums[i - 1] < 0?,?f[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1?,?g[i] = f[i - 1] + 1?;
- 若 ?nums[i - 1] == 0?,?f[i] = g[i] = 0?。
- 初始條件:?f[0] = g[0] = 0?。
- 結果提取:遍歷過程中記錄 ?f[i]??的最大值。
?
代碼實現
?

class Solution {
public:int getMaxLen(vector<int>& nums) {int n = nums.size();vector<int> f(n + 1);vector<int> g(n + 1);int ret = 0;for (int i = 1; i <= n; i++) {if (nums[i - 1] > 0) {f[i] = f[i - 1] + 1;g[i] = (g[i - 1] == 0) ? 0 : g[i - 1] + 1;} else if (nums[i - 1] < 0) {f[i] = (g[i - 1] == 0) ? 0 : g[i - 1] + 1;g[i] = f[i - 1] + 1;} else {f[i] = 0;g[i] = 0;}ret = max(ret, f[i]);}return ret;}
};


五、等差數列劃分(LeetCode 413)


?
問題描述
?
如果一個數列至少有三個元素,且任意兩個相鄰元素之差相同,則稱該數列為等差數列。給定整數數組 ?nums?,返回數組中所有為等差數組的子數組個數。
?
分析思路
?
- 狀態定義:?dp[i]??表示以 ?nums[i]??結尾的等差數列的個數。
- 狀態轉移:若 ?nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]?,說明以 ?nums[i]??結尾能形成新的等差數列,?dp[i] = dp[i - 1] + 1?;否則 ?dp[i] = 0?。
- 初始條件:?dp[0] = dp[1] = 0?,因為前兩個元素無法形成至少三個元素的等差數列。
- 結果提取:將所有 ?dp[i]??相加,得到總的等差數列子數組個數。
?
代碼實現

??
class Solution {
public:int numberOfArithmeticSlices(vector<int>& nums) {int n = nums.size();if (n < 3) return 0;vector<int> dp(n);dp[0] = dp[1] = 0;int sum = 0;for (int i = 2; i < n; i++) {if (nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]) {dp[i] = dp[i - 1] + 1;} else {dp[i] = 0;}sum += dp[i];}return sum;}
};

六、最長湍流子數組(LeetCode 978)


?
問題描述
?
給定整數數組 ?arr?,返回其最大湍流子數組的長度。湍流子數組是指相鄰元素對之間比較符號翻轉的子數組。
?
分析思路
?
- 狀態定義:?f[i]??表示以 ?arr[i]??結尾,且最后是上升(?arr[i] > arr[i - 1]?)的湍流子數組長度;?g[i]??表示以 ?arr[i]??結尾,且最后是下降(?arr[i] < arr[i - 1]?)的湍流子數組長度。
- 狀態轉移:
- 若 ?arr[i] > arr[i - 1]?,?f[i] = g[i - 1] + 1?,?g[i] = 1?;
- 若 ?arr[i] < arr[i - 1]?,?g[i] = f[i - 1] + 1?,?f[i] = 1?;
- 若 ?arr[i] == arr[i - 1]?,?f[i] = g[i] = 1?。
- 初始條件:?f[0] = g[0] = 1?,單個元素自身是長度為 1 的湍流子數組。
- 結果提取:遍歷過程中記錄 ?f[i]??和 ?g[i]??中的最大值。
?
代碼實現

??
class Solution {
public:int maxTurbulenceSize(vector<int>& arr) {int n = arr.size();vector<int> f(n, 1);vector<int> g(n, 1);int ret = 1;for (int i = 1; i < n; i++) {if (arr[i] > arr[i - 1]) {f[i] = g[i - 1] + 1;g[i] = 1;} else if (arr[i] < arr[i - 1]) {g[i] = f[i - 1] + 1;f[i] = 1;} else {f[i] = g[i] = 1;}ret = max(ret, max(f[i], g[i]));}return ret;}
};

七、單詞拆分(LeetCode 139)


?
問題描述
?
給定字符串 ?s??和字符串列表 ?wordDict??作為字典,判斷是否可以利用字典中出現的一個或多個單詞拼接出 ?s?。
?
分析思路
?
- 狀態定義:?dp[i]??表示字符串 ?s??的前 ?i??個字符是否能被字典拼接而成。
- 狀態轉移:對于每個 ?i?,枚舉分割點 ?j?(從 ?i??到 ?1?),若 ?dp[j - 1]??為 ?true?(前 ?j - 1??個字符能被拼接)且 ?s??的 ?j??到 ?i??子串在字典中,則 ?dp[i] = true?。
- 初始條件:?dp[0] = true?,空字符串能被拼接。
- 結果提取:?dp[s.size()]??即為最終結果,表示整個字符串是否能被拼接。
?
代碼實現

class Solution {
public:bool wordBreak(string s, vector<string>& wordDict) {unordered_set<string> hash;for (auto &e : wordDict) hash.insert(e);int n = s.size();vector<bool> dp(n + 1);dp[0] = true;s = " " + s;for (int i = 1; i <= n; i++) {for (int j = i; j >= 1; j--) {if (dp[j - 1] && hash.count(s.substr(j, i - j + 1))) {dp[i] = true;break;}}}return dp[n];}
};


?八、環繞字符串中唯一的子字符串(LeetCode 467)


?
問題描述
?
定義字符串 ?base??為 ?"abcdefghijklmnopqrstuvwxyz"??無限環繞的字符串,給定字符串 ?s?,統計并返回 ?s??中有多少不同非空子串也在 ?base??中出現。
?
分析思路
?
- 狀態定義:?dp[i]??表示以 ?s[i]??結尾的符合 ?base??規律的子串長度。
- 狀態轉移:若 ?s[i - 1]??到 ?s[i]??符合 ?base??的連續規律(如 ?s[i - 1]??是 ?'z'??且 ?s[i]??是 ?'a'?,或 ?s[i] = s[i - 1] + 1?),則 ?dp[i] = dp[i - 1] + 1?;否則 ?dp[i] = 1?。
- 去重處理:用長度為 26 的數組 ?hash??記錄每個字符結尾的最長符合規律子串長度,最后將所有 ?hash??元素相加,得到不同子串的個數(因為最長長度包含了所有更短的以該字符結尾的符合規律子串)。
?
代碼實現
???

class Solution {
public:int findSubstringInWraproundString(string s) {int n = s.size();vector<int> dp(n, 1);for (int i = 1; i < n; i++) {if (s[i - 1] + 1 == s[i] || (s[i - 1] == 'z' && s[i] == 'a')) {dp[i] += dp[i - 1];}}int hash[26] = {0};for (int i = 0; i < n; i++) {hash[s[i] - 'a'] = max(hash[s[i] - 'a'], dp[i]);}int sum = 0;for (auto e : hash) {sum += e;}return sum;}
};

總結

動態規劃在子數組、子串問題中,核心是定義合適的狀態,并找到清晰的狀態轉移方程。不同問題的狀態定義角度不同,有的關注以當前元素結尾的子數組的某種屬性(和、乘積、長度等),有的關注全局的可能性(如是否能拼接)。通過多練習這類題目,能逐漸掌握動態規劃的思維方式,更高效地解決復雜問題。

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

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

相關文章

微信小程序開發筆記(01_小程序基礎與配置文件)

ZZHow(ZZHow1024) 參考課程: 【尚硅谷微信小程序開發教程】 [https://www.bilibili.com/video/BV1LF4m1E7kB] 009_文件和目錄結構介紹新建頁面與調試基礎庫 一個完整的小程序項目分為兩個部分&#xff1a;主體文件、頁面文件 主體文件又稱全局文件&#xff0c;能夠作用于整…

NLP Subword 之 BPE(Byte Pair Encoding) 算法原理

本文將介紹以下內容&#xff1a; 1. BPE 算法核心原理2. BPE 算法流程3. BPE 算法源碼實現DemoBPE最早是一種數據壓縮算法&#xff0c;由Sennrich等人于2015年引入到NLP領域并很快得到推廣。該算法簡單有效&#xff0c;因而目前它是最流行的方法。GPT-2和RoBERTa使用的Subword算…

CSS 偽類選擇器

偽類選擇器&#xff08;pseudo-class selector&#xff09;是一種用于選擇HTML元素特定狀態或特征的關鍵字&#xff0c;它允許開發者基于文檔樹之外的信息&#xff08;如用戶交互、元素位置或狀態變化&#xff09;來選擇元素并應用樣式。偽類選擇器以冒號(:)開頭&#xff0c;附…

Electron 新特性:2025 版本更新解讀

引言&#xff1a;Electron 新特性在 2025 版本更新中的解讀核心價值與必要性 在 Electron 框架的持續演進中&#xff0c;新特性的引入是推動桌面開發創新的核心動力&#xff0c;特別是 2025 年的版本更新&#xff0c;更是 Electron 項目從成熟生態到前沿技術的躍進之鑰。它不僅…

MyBatis從入門到面試:掌握持久層框架的精髓

MyBatis從入門到面試&#xff1a;掌握持久層框架的精髓 前言 在Java企業級應用開發中&#xff0c;持久層框架的選擇至關重要。MyBatis作為一款優秀的半自動化ORM框架&#xff0c;以其靈活的SQL定制能力和良好的性能表現&#xff0c;成為了眾多開發者的首選。本文將帶你從MyBa…

5.Three.js 學習(基礎+實踐)

Three.js 是 “WebGL 的封裝庫”&#xff0c;幫你屏蔽了底層的著色器 / 緩沖區細節&#xff0c;專注于 “3D 場景搭建”&#xff0c;開發效率高&#xff0c;是通用 3D 開發的首選。他的核心是 “場景 - 相機 - 渲染器” 的聯動邏輯&#xff0c;先掌握基礎組件&#xff0c;再學進…

消火栓設備工程量計算 -【圖形識別】秒計量

消火栓設備工程量計算 -【圖形識別】秒計量 消防系統的消火栓設備水槍、水帶和消火栓組成&#xff0c;根據清單定額規則計算消火栓設備工程量。通過CAD快速看圖的圖形識別框選圖紙就能自動數出消火栓數量&#xff0c;省時又準確&#xff0c;是工程人做消防算量的好幫手。 一、…

Docker 與 VSCode 遠程容器連接問題深度排查與解決指南

Docker 與 VSCode 遠程容器連接問題深度排查與解決指南 引言 Visual Studio Code 的 Remote - Containers 擴展極大地提升了開發體驗&#xff0c;它將開發環境容器化&#xff0c;保證了環境的一致性&#xff0c;并允許開發者像在本地一樣在容器內進行編碼、調試和運行。然而&…

愛圖表:鏑數科技推出的智能數據可視化平臺

本文轉載自&#xff1a;https://www.hello123.com/aitubiao ** 一、? AI 圖表&#xff1a;智能數據可視化好幫手 愛圖表是鏑數科技旗下的一款智能數據可視化工具&#xff0c;它能讓復雜的數字和報表變得直觀又好懂。接入了先進的DeepSeek 系列 AI 模型&#xff0c;它不僅會做…

ENVI系列教程(四)——圖像幾何校正

目錄 1 概述 1.1 控制點選擇方式 1.2 幾何校正模型 1.3 控制點的預測與誤差計算 2 詳細操作步驟 2.1 掃描地形圖的幾何校正 2.1.1 第一步:打開并顯示圖像文件 2.1.2 第二步:啟動幾何校正模塊 2.2 Landsat5 影像幾何校正 2.2.1 第一步:打開并顯示圖像文件 2.2.2 第…

STM32-FreeRTOS操作系統-消息隊列

引言在嵌入式開發領域&#xff0c;STM32與FreeRTOS的結合應用極為廣泛。本文將探討如何在STM32上使用FreeRTOS實現消息隊列功能&#xff0c;助力高效任務通信與系統協作。消息隊列定義消息隊列是一種在 FreeRTOS 中用于任務間通信的機制。它允許任務將消息發送到隊列中&#xf…

【開題答辯全過程】以 C語言程序設計課程網站為例,包含答辯的問題和答案

個人簡介一名14年經驗的資深畢設內行人&#xff0c;語言擅長Java、php、微信小程序、Python、Golang、安卓Android等開發項目包括大數據、深度學習、網站、小程序、安卓、算法。平常會做一些項目定制化開發、代碼講解、答辯教學、文檔編寫、也懂一些降重方面的技巧。感謝大家的…

手機上有哪些比較好用的待辦事項提醒工具

在快節奏的現代工作中&#xff0c;我們每天都要面對大量的任務與事務。從項目截止日期、客戶會議&#xff0c;到日常的工作安排&#xff0c;瑣碎的事項容易讓人顧此失彼。 手機待辦事項工具早已突破傳統“記事本”的局限&#xff0c;成為移動辦公場景下的效率核心。它們通過任務…

Mysql數據庫事務全解析:概念、操作與隔離級別

MySQL系列 文章目錄MySQL系列一、什么是事務1.1事務的核心概念1.2、 事務的四大屬性&#xff08;ACID&#xff09;1.2.1 原子性&#xff08;Atomicity&#xff09;1.2.2 一致性&#xff08;Consistency&#xff09;1.2.3 隔離性&#xff08;Isolation&#xff09;1.2.4 持久性&…

【MCU EEPROM開發教程】

簡單來說把eeprom芯片當成一個傳感器來使用&#xff0c;通過IIC/SPI等協議對芯片進行讀寫操作&#xff0c;具體的讀寫操作涉及到一些算法—怎么樣讀寫更加快速&#xff0c;以及一些異常錯誤處理。 應用場景&#xff1a; 對于一些掉電也不能丟失的數據要存在eeprom/flash中&…

Docker將鏡像搬移到其他服務上的方法

導出/加載鏡像&#xff08;保留分層、標簽&#xff09;和導出/導入容器快照&#xff08;僅文件系統&#xff0c;丟失鏡像歷史與標簽&#xff09;。 一、把鏡像打包帶走&#xff08;推薦&#xff09; 適合把一個或多個鏡像搬到離線/內網機器&#xff0c;保留分層與標簽。 在源服…

Ubuntu 系統安裝 Miniconda 完整方法與注意事項

一、完整安裝步驟 1. 下載 Miniconda 安裝包 Miniconda 安裝包為 .sh 格式腳本,下載途徑分兩種: 方式 1:瀏覽器下載(適合新手) 訪問 Miniconda 官方下載頁,選擇對應系統版本(Ubuntu 選 Miniconda3-latest-Linux-x86_64.sh),默認保存到用戶目錄的 ~/Downloads 文件夾…

【后端】數據庫四大范式詳細解析

梳理一下 MySQL&#xff08;或關系型數據庫&#xff09;中的第一、二、三、四范式&#xff0c;這是數據庫設計中非常重要的規范化理論。1?? 第一范式 (1NF&#xff1a;First Normal Form)定義&#xff1a;字段具有原子性&#xff0c;不可再分。數據表中每一列都必須是不可分割…

HarmonyOS后臺任務調度:JobScheduler與WorkManager實戰指南

本文將深入探討HarmonyOS 5&#xff08;API 12&#xff09;中的后臺任務調度機制&#xff0c;重點講解JobScheduler和WorkManager的使用方法、適用場景及最佳實踐&#xff0c;幫助開發者實現高效、智能的后臺任務管理。 1. 后臺任務調度概述 HarmonyOS提供了兩種主要的后臺任務…

Prompt工程實踐

你在寫prompt時候&#xff0c;是不是總覺得大模型它不聽話。要么答非所問、要么一堆廢話。扒開思考過程仔細閱讀時而覺得它聰明絕頂&#xff0c;時而又覺得它愚蠢至極。明明已經對了怎么又推理到錯的地方去了&#xff0c;明明在提示詞中提醒過了不要這么思考它怎么就瞎想了。這…