日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《dp补卡——子序列问题》

發(fā)布時間:2023/12/1 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《dp补卡——子序列问题》 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 300. 最長遞增子序列
  • 674. 最長連續(xù)遞增序列
  • 718. 最長重復子數(shù)組
  • 1143. 最長公共子序列
  • 53. 最大子序和
  • 392. 判斷子序列
  • 115. 不同的子序列
  • 583. 兩個字符串的刪除操作
  • 72. 編輯距離
  • 647. 回文子串 (與 5.最長回文子串思路差不多)
  • 516. 最長回文子序列

300. 最長遞增子序列

step1:dp[i]:下標<=i的最長子序列長度。即以 nums[i] 結尾 的「上升子序列」的長度,注意以nums[nums.size()-1]結尾的上升子序列長度并不一定是全局最優(yōu)值,所以要從dp[i]中找到最大值。
step2:狀態(tài)轉移方程:

if(nums[i] > nums[j]) dp[i] = max(dp[i],dp[j]+1); //取得dp[j]+1中最大的那個值作為dp[i]

step3:初始化
每一個i,至少都是1.
step4:遍歷順序,從前向后遍歷。

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

674. 最長連續(xù)遞增序列

簡單題,迅速AC。不過這個是貪心的思路。

class Solution { public:int findLengthOfLCIS(vector<int>& nums) {int n = nums.size();if(n == 0) return 0;int len=1;int maxlen =1;for(int i = 1; i < n; i++){if(nums[i] > nums[i-1]){len+=1;}elselen = 1;maxlen = max(maxlen,len);}return maxlen;} };

下面使用dp思路:
step1:dp[i]以下標為i結尾的數(shù)組連續(xù)遞增的子序列長度為dp[i]
step2: 如果nums[i+1] > nums[i],那么以i+1結尾的連續(xù)遞增的子序列長度一定等于以i結尾的數(shù)組的連續(xù)遞增的子序列長度+1
即dp[i+1] = dp[i] + 1
否則,dp不進行賦值,同時需要注意以nums.size()-1結尾的連續(xù)遞增子序列長度不一定為全局最優(yōu)值。

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

718. 最長重復子數(shù)組

class Solution { public:int findLength(vector<int>& nums1, vector<int>& nums2) {int len1 = nums1.size();int len2 = nums2.size();vector<vector<int>> dp(len1,vector<int>(len2,0));int result = 0;for(int i = 0; i < len1; i++){for(int j = 0; j < len2; j++){if(nums1[i] == nums2[j]){if(i == 0 || j == 0){dp[i][j] = 1;}elsedp[i][j] = dp[i-1][j-1]+1;}result = max(result,dp[i][j]);}}return result;} };

1143. 最長公共子序列

這里不要求子序列是連續(xù)的,但是要相對有順序。
step1:
dp[i][j]:長度為[0,i]的字符串text1與長度為[0,j]的字符串text2的最長公共子序列長度
step2:

if(text1[i] == text[j]) {//找到了一個公共元素dp[i][j] = dp[i-1][j-1]+1; } else {//沒有找到公共元素,就選擇dp[i-1][j]與dp[i][j-1]中最大的dp[i][j] = max(dp[i-1][j],dp[i][j-1]); }


step3:初始化比較麻煩。

for(int i = 0; i < len1; i++) {if( (i >= 1 && dp[i-1][0] == 1 ) || (text1[i] == text2[0]) ) dp[i][0] = 1; } for(int j = 0; j < len2; j++) {if( (j >= 1 && dp[0][j-1] == 1 ) || (text2[j] == text1[0]) ) dp[0][j] = 1; }

AC代碼:

class Solution { public:int longestCommonSubsequence(string text1, string text2) {int len1 = text1.size();int len2 = text2.size();vector<vector<int>> dp(len1,vector<int>(len2,0));int maxlen = 0;//初始化for(int i = 0; i < len1; i++){if( (i >= 1 && dp[i-1][0] == 1 ) || (text1[i] == text2[0]) ) dp[i][0] = 1;}for(int j = 0; j < len2; j++){if( (j >= 1 && dp[0][j-1] == 1 ) || (text2[j] == text1[0]) ) dp[0][j] = 1;}//狀態(tài)轉移for(int i = 1; i < len1; i++){for(int j = 1; j < len2; 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[len1-1][len2-1];} };

53. 最大子序和

step1:
dp[i],下標<=i的最大和。
step2:

if(dp[i-1] >= 0) dp[i] = dp[i-1] + nums[i]; else dp[i] = nums[i];

step3:
dp[0] = nums[0]
AC代碼:

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

392. 判斷子序列

這一題與1143. 最長公共子序列基本是一個題目,不過需要加入一些邊界輸入判斷,如果最長公共子序列長度等于短序列的長度就認為是子序列。
當然更加精準來說,兩者的狀態(tài)轉移方程不同。
if (s[i] != t[j]), 說明此時長序列t要刪除元素,把t[j]刪除,則dp[i][j]則是s[i]與t[j-1]相比較了。
if(s[i] == t[j]),說明此時結果為dp[i][j]+1
至于為什么if (s[i] != t[j]),不能是從dp[i-1][j]推導得到,這是因為,dp[i-1][j]相當于是短序列刪除元素s[i]從而與長序列保持一致。而題目中短序列是不能刪除元素的。

圖1 最長公共子序列 圖2 判斷子序列
class Solution { public:bool isSubsequence(string text1, string text2) {int len1 = text1.size();int len2 = text2.size();if(len1 == 0) return true;if(len2 < len1) return false;vector<vector<int>> dp(len1,vector<int>(len2,0));int maxlen = 0;//初始化for(int i = 0; i < len1; i++){if( (i >= 1 && dp[i-1][0] == 1 ) || (text1[i] == text2[0]) ) dp[i][0] = 1;}for(int j = 0; j < len2; j++){if( (j >= 1 && dp[0][j-1] == 1 ) || (text2[j] == text1[0]) ) dp[0][j] = 1;}//狀態(tài)轉移for(int i = 1; i < len1; i++){for(int j = 1; j < len2; j++){if(text1[i] == text2[j])dp[i][j] = dp[i-1][j-1]+1;else dp[i][j] = dp[i][j-1];}}return dp[len1-1][len2-1] == len1;} };

115. 不同的子序列

step1:dp[i][j]:以i為結尾的s序列中出現(xiàn)以j結尾的t子序列的個數(shù)。
step2:
分析兩種情況:
case1:s[i] 等于 t[j]
此時dp[i][j]由兩部分組成:
一部分是用s[i]匹配,則dp[i][j] = dp[i-1][j-1]
一部分不用s[i]匹配,則dp[i][j] = dp[i-1][j]
如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]來匹配,即用s[0]s[1]s[2]組成的bag。
所以:

dp[i][j] = dp[i-1][j-1] + dp[i-1][j];

case2:s[i] 不等于 t[j]
此時,dp[i][j]只能由dp[i-1][j]推導來
即:dp[i][j] = dp[i-1][j]

step3:
我們已經直到狀態(tài)由dp[i-1][j] 或者 dp[i-1][j-1]
所以dp[i][0] 和 dp[0][j]都一定要初始化。
dp[i][0]:代表以下標i結尾的s序列出現(xiàn)以下標0為結尾的子序列個數(shù)。

int times = 0; for(int i = 0; i < len1; i++) {if(s[i] == t[0]) times++;dp[i][0] = times; }

dp[0][j]: 代表以下標0結尾的s序列出現(xiàn)以下標j為結尾的子序列個數(shù)。

if(s[0] == t[0]) dp[0][0] = 1;

其他情況均為0.

需要注意的地方:由于dp內存的數(shù)目過大,需要用long long
,并且對與求和的結果,需要對INT_MAX取模不然會溢出.

class Solution { public:int numDistinct(string s, string t) {int len1 = s.size();int len2 = t.size();if(len2 > len1) return 0;vector<vector<long long>> dp(len1,vector<long long>(len2,0));//初始化int times = 0;for(int i = 0; i < len1; i++){if(s[i] == t[0]) times++;dp[i][0] = times;}//遞推for(int i = 1; i < len1; i++){for(int j = 1; j < len2; j++){if(s[i] == t[j])dp[i][j] = (dp[i-1][j-1] + dp[i-1][j])%INT_MAX;elsedp[i][j] = dp[i-1][j];}}return dp[len1-1][len2-1];} };

583. 兩個字符串的刪除操作

這一題,和上一題類似,不過狀態(tài)轉移的時候需要多考慮一點點。
還有就是dp數(shù)組定義與上面有點區(qū)別,這樣對于初始化的時候稍微簡便了。
step1:dp[i][j]:以i-1為結尾的字符串word1與以j-1為結尾的字符串word2,想要達到相等,所需要刪除元素的最少次數(shù)。
step2:
分為兩個情況:
case1:word1[i-1]與word2[j-1]相同的時候
此時dp[i][j] = dp[i-1][j-1]
case2:word1[i-1]與word2[j-1]相同的時候
此時存在三個子情況:

  • case1:刪除word1[i-1]后,有相等機會,此時dp[i][j] = dp[i-1][j] + 1
  • case2:刪除word2[j-1]后,有相等機會,此時dp[i][j] = dp[i][j-1] + 1
  • case3:同時刪除word1[i-1]與word2[j-1],有相等機會,此時dp[i][j] = dp[i-1][j-1] + 2
    所以此時有
dp[i][j] = min{dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+2};

step3:
關于初始化,分為dp[i][0]與dp[0][j]
dp[i][0]:word2為空字符串,以i-1結尾的字符串word1要刪除i個元素,才能與word2相同。所以
dp[i][0] = i
dp[0][j]:word1為空字符串,以j-1結尾的字符串word2要刪除j個元素,才能與word1相同。所以
dp[0][j] = j

AC代碼:

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

72. 編輯距離

step1:dp[i][j]表示以下標i-1為結尾的字符串word1,和以下標j-1為結尾的字符串word2,最近編輯距離為dp[i][j]
step2:遞推公式
仍然分為兩種情況:
word1[i-1] == word2[j-1],此時不需要操作,dp[i][j] = dp[i-1][j-1]
word1[i-1] != word2[j-1],此時進行增、刪、換

  • case1:word1增加一個元素,使得word1[i-1]與word2[j-1]相同,此時dp[i][j] = dp[i-1][j]+1
  • case2:word1刪除一個元素(等同于word2增加一個元素),使得word1[i-1]與word2[j-1]相同,此時dp[i][j] = dp[i][j-1]+1
  • case3:替換元素,word1替換word1[i-1],使得word1[i-1]與word2[j-1]相同,此時dp[i][j] = dp[i-1][j-1]+1
    所以遞推公式如下:
if(word1[i-1] == word2[j-1])dp[i][j] = dp[i-1][j-1]; elsedp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;

step3:初始化
需要初始化dp[i][0] 和 dp[0][j]
dp[i][0]:下標為i-1結尾的字符串word1和空字符串word2的最近距離,此時dp[i][0] = i
同理dp[0][j] =j

class Solution { public:int minDistance(string word1, string word2) {int len1 = word1.size();int len2 = word2.size();vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));//初始化for(int i = 0; i <= len1; i++)dp[i][0] = i;for(int j = 0; j <= len2; j++)dp[0][j] = j;//開始遞推for(int i = 1; i <= len1; i++){for(int j = 1; j <= len2; 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-1][j],dp[i][j-1])) + 1;}}return dp[len1][len2];} };

647. 回文子串 (與 5.最長回文子串思路差不多)

step1:dp[i][j],下標在[i,j]之間的字符串中是否為回文子串
step2:遞推公式
if(s[i] == s[j])

  • case1 :下標i==j,說明是同一個字符,為true
  • case2 :下標i與j相差等于1,為true
  • case3 :下標i與j相差大于1,看[i+1,j-1]區(qū)間是否為true
if(s[i] == s[j]) {if( (j-i <= 1) || dp[i+1][j-1] == true ){result++;dp[i][j] = true;} }

if(s[i] != s[j]) dp[i][j] = false
step3:初始化
dp[i][j]初始化為false
step4:遍歷順序
在遞推公式可以看出,case3是根據(jù)dp[i+1][j-1]推導出來的。所以不能從上到小、從左到右遍歷。
從下到上,從左到右遍歷,保證dp[i+1][j-1]在dp[i][j]之前運算。

for(int i = s.size() - 1; i >= 0; i--) {for(int j = i; j < s.size(); j++){ } }

注意,回顧dp[i][j]數(shù)組定義:下標在[i,j]之間的字符串中是否為回文子串,所以j必定>=i。
AC代碼:

class Solution { public:int countSubstrings(string s) {int len = s.size();vector<vector<bool>> dp(len,vector<bool>(len,false));int result = 0;for(int i = len - 1; i >= 0; i--){for(int j = i; j < len; j++){if(s[i] == s[j]){if( j - i <= 1 || dp[i+1][j-1] == true){result++;dp[i][j] = true;}}}}return result;} };

516. 最長回文子序列

step1:dp[i][j]:下標在[i,j]內的最長回文子序列的長度為dp[i][j]
step2:
如果s[i] == s[j]那么dp[i][j] = dp[i+1][j-1] + 2;
如果s[i] != s[j],說明s[i] 和 s[j]的同時加入并不能增加[i,j]區(qū)間的長度,那么分別加入s[i]、s[j]看看哪個可以組成最長回文子序列。
加入s[j]的回文子序列長度為dp[i+1][j]
加入s[i]的回文子序列長度為dp[i]][j-1]
dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
step3:初始化
初始為0。當i==j時,該字符串為單個字符,回文子序列長度為1,即dp[i][j] = 1
step4:遍歷順序
依靠dp[i+1][j-1]、dp[i+1][j]、dp[i][j-1],所以遍歷i從下到上。j從左到右。
根據(jù)dp數(shù)組定義:下標在[i,j]內的最長回文子序列的長度為dp[i][j]
所以j>=i。

class Solution { public:int longestPalindromeSubseq(string s) {int len = s.size();vector<vector<int>> dp(len,vector<int>(len,0));for(int i = 0; i < len; i++) dp[i][i] = 1;for(int i = len - 1; i >= 0; i--){for(int j = i + 1; j < len; 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]);}}return dp[0][len-1];} };

總結

以上是生活随笔為你收集整理的《dp补卡——子序列问题》的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。