二刷力扣——DP算法(子序列問題)

300. 最長遞增子序列

定義是以本元素結尾,所以公式初始化都好弄。但是太慢

class Solution {public int lengthOfLIS(int[] nums) {int n=nums.length;int[] dp = new int[n];//以自己結尾的最長遞增子序列dp[0]=1;int maxzi=1;for(int i=1;i<n;++i){dp[i]=1;for(int j=0;j<i;++j){if(nums[i]>nums[j])dp[i]=Math.max(dp[i],dp[j]+1);} maxzi=Math.max(maxzi,dp[i]);}return maxzi;}
}

時間O(n^2),空間O(n)

看題解的優化:

貪心+二分查找:

下面鏈接說的很清楚:

https://writings.sh/post/longest-increasing-subsequence-revisited

中心思想是:

7bc063999cfd400986b22078eed5da2b.png

所以這里的d數組定義是:d[i]是長度為 i 的遞增子序列的最小結尾元素。比如d[2]=6,所以長度為2 的遞增子序列里面,結尾元素最小的情況是2。

所以挨個遍歷nums數組來更新這個d數組,注意這個d數組是動態的,不像之前動態規劃的DP數組,我們得記錄他的長度,因為遇到了nums某元素比d最后的結尾還大,就要把這個某元素加入到d數組,擴大d數組。。如果遇到某元素比結尾小,對應d數組定義,那d數組里面肯定有某個值要更新為這個nums[i],這個值是哪個位置?就是把比nums[i]大的&最接近nums[i]的 換成nums[i]。舉例下圖。遇到3比d 的結尾8要小,所以更新d? 的某個元素,找到比3大的&最接近3 的元素是6,so替換。這樣保證d[2]=3,對應長度為2的遞增子序列的最小結尾元素是3(2、3這個子序列)。

f9ab93768f754176b843a471d640e040.png

所以在過程中,d數組長度不會減少,所以p數組裝的nums元素 的個數,就是所要求的最長遞增子序列長度,d數組的最后結尾元素代表了一條遞增最慢的子序列,所以這個元素的下標,也就是len就是結果。

還有一個解釋:

1cbc84e833f4447cb6d9eebf4c8ec244.png

代碼注意,沒有寫==的時候break,所以退出循環一定是left=right+2了,這個時候left的位置就是 比nums[i]大的&最接近nums[i]的 位置,然后直接替換成nums[i]。

len是為了記錄包含nums元素 的個數,因為這里沒有使用大小可動態變化的集合。

class Solution {public int lengthOfLIS(int[] nums) {int n=nums.length;if(n==0)return 0;int len =1;int[] d = new int[n+1];//長度為i的遞增子序列的最小結尾元素d[len]=nums[0];for(int i=1;i<n;++i){if(nums[i]>d[len])//加到結尾去{d[++len]=nums[i];}else//在d[1]-d[len]中找到更改為nums[i]后使得d仍然有序的位置;二分查找更快{int l=1,r=len;//左閉右閉while(l<=r){int mid=(l+r)>>1;if(d[mid]<nums[i]){l=mid+1;}else r=mid-1;}d[l]=nums[i];//替換}}return len;}
}

貪心遍歷nums,動態規劃遍歷dp,給固定大小的數組的每個元素求解值。

674. 最長連續遞增序列

可以DP數組,也可以貪心省存儲。

貪心:用start記錄連續子序列的開頭下標,發現連續遞增在i 這里斷了,就更新start為i(重新一個遞增序列)。然后取最大值

class Solution {public int findLengthOfLCIS(int[] nums) {int n=nums.length,maxCount=1;int start=0;for(int i=1;i<n;++i){if(nums[i]<=nums[i-1]){start=i;}maxCount=Math.max(maxCount,i-start+1);}return maxCount; }
}

718. 最長重復子數組

dp[i][j]:如果不想單獨初始化的話,dp設置比nums偏移一個坐標,是以nums1[i-1]和nums2[j-1]結尾的子數組最長 長度;不偏移的話是nums1[i]和nums2[j]結尾的子數組最長 長度

公式:如果遍歷的兩個元素相等,可以在上一個dp[i-1][j-1]的基礎上拼接這個元素,所以+1

?

偏移:

class Solution {public int findLength(int[] nums1, int[] nums2) {int m=nums1.length,n=nums2.length;int[][] dp=new int[m+1][n+1];//以nums1[i-1]和nums2[j-1]結尾的子數組最長 長度int maxCount=0;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(nums2[j-1]==nums1[i-1]){dp[i][j]=dp[i-1][j-1]+1;maxCount=Math.max(dp[i][j],maxCount);}}}return maxCount;}
}

還可以用滾動數組實現,注意跟 01背包的滾動數組實現 一樣,留下的那個維度只能在內循環,而且得從大到小,因為得是上一輪的值而不是這一輪更新過的值。

而且不相等的時候,要把dp[j]清零,因為二維的時候不操作dp[i][[j]直接就是0;但是一維的時候不操作是繼承了上一輪的值,是錯的。只要不相等以nums1[i-1]結尾的子數組長度就是0,所以置零。

class Solution {public int findLength(int[] nums1, int[] nums2) {int m=nums1.length,n=nums2.length;int[] dp=new int[m+1];//以nums1[i-1] 結尾的子數組最長 長度int maxCount=0;for(int i=1;i<=n;++i){for(int j=m;j>=1;--j)//留下維度從大到小{if(nums2[i-1]==nums1[j-1]){dp[j]=dp[j-1]+1;maxCount=Math.max(dp[j],maxCount);}else dp[j]=0;   //不相等了,清零}}return maxCount;}
}

?

不偏移:

class Solution {public int findLength(int[] nums1, int[] nums2) {int m=nums1.length,n=nums2.length;int[][] dp=new int[m ][n ];//nums1[i]和nums2[j]結尾的子數組最長 長度int maxCount=0;for(int i=0;i<n;++i){dp[0][i]=nums1[0]==nums2[i]?1:0;maxCount=Math.max(dp[0][i],maxCount);}for(int i=0;i<m;++i){dp[i][0]=nums1[i]==nums2[0]?1:0;maxCount=Math.max(dp[i][0],maxCount);}for(int i=1;i<m;++i){for(int j=1;j<n;++j){if(nums2[j]==nums1[i]){dp[i][j]=dp[i-1][j-1]+1;maxCount=Math.max(dp[i][j],maxCount);}}}return maxCount;}
}

?

1143. 最長公共子序列

dp[i][j]的定義跟上面不一樣,這里子序列是不連續的,如果定義還要求是以元素結尾的子序列長度的話,i元素=j元素,dp[i][j]不一定是在dp[i-1][j-1]的基礎上+1,就會很麻煩。

所以設置為:text1[0,i-1]和text2[0,j-1]范圍內,最長公共子序列的長度.

最后返回的就是dp[m][n]

遞推公式:如果相等的話,是在dp[i-1][j-1]的基礎上+1。

如果不相等,不能直接跳過,比如abcde,ace,到了abc、ace的時候c!=e,那么dp[3][3]按照定義應該是2,等于dp[3][2]。把abc、ace倒過來,dp[3][3]又=dp[2][3]。A[i-1]和B[j-1]不相等,但是A[i-1]可能和B[j-1]之前的相等,B[j-1]可能和A[i-1]之前的相等,所以要取這兩種情況的最大值。

采用跟上提一樣的偏移寫法:

class Solution {public int longestCommonSubsequence(String text1, String text2) {int m=text1.length(),n=text2.length();int[][] dp=new int[m+1][n+1];//text1[0,i-1]和text2[0,j-1]范圍內,最長公共子序列的長度for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(text1.charAt(i-1)==text2.charAt(j-1)){dp[i][j]=dp[i-1][j-1]+1;}else dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);}}return dp[m][n];}
}

同樣可以用滾動數組嗎?

不行,

c1b233ccbae44bc3b7a8e21d12deb35c.png

dp[i][j]取決于這三個元素,如果用滾動數組,無論行優先,還是列優先遍歷,dp[i][j-1]/dp[i-1][j]都會把dp[i][j]覆蓋。

1035. 不相交的線

跟上題一樣。

392. 判斷子序列

編輯距離問題 的入門題目。用上面的方法然后判斷dp[m][n]是否=s.length()的話,效率低。

?

所以也可以用雙指針做一下:

class Solution {public boolean isSubsequence(String s, String t) {int m=s.length(),n=t.length();int sp=0,tp=0;while(sp<m && tp<n)//sp:s已經匹配了的右邊界{if(s.charAt(sp)==t.charAt(tp)){sp++;}tp++;}if(sp==m)return true;return false;}
}

時間O(n)。

有一個進階問題;

95c0ade49a9d4c50a960ee0a0522aa79.png

力扣題解給出一個跟S 無關的預處理DP數組的做法:dp[i][j] 表示字符串 t 中從位置 i 開始往后字符 j 第一次出現的位置。

遞推公式:

e688bf5962aa4d4783628f4c9783d233.png

初始化:有效的下標 i 就是0到n-1,所以多設置一個n ,讓dp[n][j]都=n,代表位置不存在。這是判斷false的條件。

遍歷順序:得到下標 i到n-1 的第一次位置,所以i 需要從大到小。

class Solution {public boolean isSubsequence(String s, String t) {int m=s.length(),n=t.length();int[][] dp=new int[n+1][26];//字符串 t 中從位置 i 開始往后字符 j 第一次出現的位置//初始化for(int i=0;i<26;++i){dp[n][i]=n;//代表不存在}for(int i=n-1;i>=0;--i)//和s無關,得到dp數組{for(int j=0;j<26;++j){if(t.charAt(i)==j+'a'){dp[i][j]=i;//更新位置}else dp[i][j]=dp[i+1][j];}}//檢查S1、S2……int add=-1;for(int i=0;i<m;++i){add=dp[add+1][s.charAt(i)-'a'];//以上一次找到的位置的下一個為起點System.out.println(add);if(add==n)return false;}return true;}
}

過程大概就是:

s = "abc", t = "ahbgdc"

s[0]=a在t[0,n-1]的第一個位置, 是0;

s[1]=b在t[1,n-1]的第一個位置,是2;

s[2]=c在t[3,n-1]的第一個位置,是5;

保證了s各字母在t中找到的位置是遞增的,而且這個位置不等于 代表不存在的n,就返回true。

115. 不同的子序列

仔細看題目,在s子序列t 出現的個數。s的子序列是不連續的,t是整個連續的。

定義dp的時候,dp[i][j]表示 在s[0-i](不一定以i結尾)中t[0,j](連續,一定以j結尾)出現的個數。

所以這里dp[i][j]有兩種可能:以s[i]結尾和不以s[i]結尾,這是分情況討論的來源。

1、當s[i-1]==t[j-1],以s[i-1]結尾的話,t[j-1]也定了,所以只能等于不包含這兩個相同元素的個數,也就是dp[i-1][j-1];

不以s[i-1]結尾的話,那就是不包含這個元素,所以要找這個元素之前子序列出現個數,即dp[i-1][j]。

遞推公式題解中的解釋,力扣給的逆序的DP:

e4033dc16c494731b0336577a235b0ff.png

2、當s[i-1]!=t[j-1],肯定只會是不以s[i-1]結尾了,所以讓s[0,i-2]和t[0,j-1]匹配,即dp[i-1][j]。

初始化:如果像上面的那樣,初始化為0,結果只會是0。先看dp[i][0],s[0-i-1]包含空串的數量,應該為1;而且空串也是空串的子串,所以dp[0][0]=1。dp[0][j],空串包含t[0,j-1]的數量,應該是0。

class Solution {public int numDistinct(String s, String t) {int m=s.length(),n=t.length();int[][] dp=new int[m+1][n+1];//在s[0-i-1](不要求以i結尾)的子序列中t[0,j-1](要以j結尾)出現的個數for(int i=0;i<=m;++i)dp[i][0]=1;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(s.charAt(i-1)==t.charAt(j-1))dp[i][j]=dp[i-1][j ]+dp[i-1][j-1];else dp[i][j]=dp[i-1][j];}}return dp[m][n];}
}

用Java不用取余也能過。

也可以用滾動數組。注意內循環也得從大到小:

fe33b2df35b84cf2a5f6d350ca31fd96.png

class Solution {public int numDistinct(String s, String t) {int m=s.length(),n=t.length();int[] dp=new int[n+1];//在s[0-i-1](不要求以i結尾)的子序列中t[0,j-1](要以j結尾)出現的個數dp[0]=1;for(int i=1;i<=m;++i){for(int j=n;j>=1;--j)//避免覆蓋上一輪的{if(s.charAt(i-1)==t.charAt(j-1))dp[j]+=dp[j-1];// else dp[j]=dp[j];}}return dp[n];}
}

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

dp[i][j]定義:1[0—i]、2[0—j ]相等的最小步數。

初始化:根據定義,dp[0][i]應該=i;同理dp[0][j]=j。

2元素相等,那肯定這一步不用刪除元素,直接等于之前的dp[i-1][j-1]

2個元素不相等的時候,需要刪除,那就有三種情況。刪一個有兩種,或者兩個都刪。既然是最小步數,所以取最小值。

class Solution {public int minDistance(String word1, String word2) {int m=word1.length(),n=word2.length();int[][] dp=new int[m+1][n+1];//1[0,i-1]、2[0,j-1]相等的最小步數for (int i = 1; i <= m; i++)dp[i][0] = i;for (int j = 1; j <= n; j++)dp[0][j] = j;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(word1.charAt(i-1)==word2.charAt(j-1)){dp[i][j]=dp[i-1][j-1];}else dp[i][j]=Math.min(Math.min(dp[i-1][j],dp[i][j-1])+1,dp[i-1][j-1]+2);}}return dp[m][n];}
}

也不能滾動數組。

?

最后,綜合題:

72. 編輯距離

相等和上面是一樣的。

不相等:(注意只能操作word1)

增:dp[i][j-1]+1。增了之后新增的和2[j-1]相等。

刪:dp[i-1][j]+1

換:dp[i-1][j-1]+1

關于增刪情況的解釋:

eb1d38ec3cf44a39b2386f20f798d41b.png

所以取這三個的最小值。

class Solution {public int minDistance(String word1, String word2) {int m=word1.length(),n=word2.length();int[][] dp=new int[m+1][n+1];//1[0,i-1]轉化成2[0,j-1]for(int i=1;i<=m;++i)dp[i][0]=i;for(int j=1;j<=n;++j)dp[0][j]=j;for(int i=1;i<=m;++i){for(int j=1;j<=n;++j){if(word1.charAt(i-1)==word2.charAt(j-1)){dp[i][j]=dp[i-1][j-1];}else {dp[i][j]=1+Math.min(dp[i-1][j-1],Math.min(dp[i][j-1],dp[i-1][j]));}}}return dp[m][n];}
}

?

編輯距離總結篇

dp大小一般都設置[m+1][n-1]。dp[i][j]代表1[i-1]和2[j-1] 的關系。是有意義的,比如1、2是字符串的話,dp[0]就涉及到空串。

1、判斷子序列:可以DP,可以雙指針。

2、不同的子序列:比較難,要把握題目意思,兩種情況都要考慮(有/無 s子序列結尾元素)。有的情況是dp幾,無的情況是dp幾。

b73eaae55aba4746a7a56be7425e1f03.png

有的話是dp[i-1][j-1]。因為i-1和j-1能對上,所以這兩個固定了,就不考慮了,只看前面的個數。

無的話是dp[i-1][j],只能確定不考慮i-1,考慮i-2及之前的;j-1不知道和誰對上,所以j-1仍然要考慮,即為dp[i-1][j]

這里的思想和編輯距離是差不多的。

3、兩個字符串刪除操作

不相等的時候,有三種情況,算好做一點。

3c8723d309504ca196d899e9371461d8.png

4、編輯距離

有了前面的鋪墊好做一些了。

27586126b5cc4daeaf5ab94254085cfd.png

要想清楚的仍是 刪/增/改 分別對應dp幾:

b229fd119be84201967861e2ea53510d.png

?

子數組,子序列問題,dp的定義要思考,要不要以當前元素結尾?結尾的話好遞推嗎?…

?

?

?

?

?

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

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

相關文章

QT中QDomDocument讀寫XML文件

一、XML文件 <?xml version"1.0" encoding"UTF-8"?> <Begin><Type name"zhangsan"><sex>boy</sex><school>Chengdu</school><age>18</age><special>handsome</special>&l…

【YOLOv5進階】——引入注意力機制-以SE為例

聲明&#xff1a;筆記是做項目時根據B站博主視頻學習時自己編寫&#xff0c;請勿隨意轉載&#xff01; 一、站在巨人的肩膀上 SE模塊即Squeeze-and-Excitation 模塊&#xff0c;這是一種常用于卷積神經網絡中的注意力機制&#xff01;&#xff01; 借鑒代碼的代碼鏈接如下&a…

在C#中使用RabbitMQ做個簡單的發送郵件小項目 _

前言 好久沒有做項目了&#xff0c;這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作&#xff0c;之前在我的個人博客里面回復評論和友鏈申請是會通過發送郵件來通知對方的&#xff0c;不過當時只是簡單的進行了異步操作。那么這次來使用RabbitMQ去統一發送郵件&#x…

vue中路由來回切換頁面直接卡死

今天發現一個很嚴重的問題&#xff0c;項目好不容易做好了&#xff0c;結果頁面多了&#xff0c;切換之后卡死。頁面所有的交互效果都失效了。 排查了許久的錯誤原因最后發現原來是路由名稱重復了。 如上圖當頁面跳轉到riskdetails詳細頁面之后&#xff0c;框架則被這個詳情頁…

隨機森林R語言預測工具

隨機森林&#xff08;Random Forest&#xff09;是一種基于決策樹的集成學習方法&#xff0c;它通過構建多個決策樹并集成它們的預測結果來提高預測的準確性。在R語言中&#xff0c;我們可以使用randomForest包來構建和訓練隨機森林模型。以下是對隨機森林的詳細介紹以及使用R語…

java高仿真數據生成器-需要的拿去

java高仿真數據生成器源碼-需要的拿去 nit-random-tools 介紹&#xff1a;高仿真數據生成器 逆天開源 java 證號碼, 姓名&#xff0c;職業, 日期&#xff0c;手機號 生成器 功能列表 編號功能描述class1號 生成器NitIdcardGenerator2姓名 生成器NitChineseNameGenerator3職…

node.lib下載失敗,手動下載并配置

在無網絡環境&#xff0c;或者網絡不好的環境&#xff0c;node.lib會下載失敗&#xff0c;此時可手動下載并進行配置。 我們以 node16.17.0 為例&#xff1a; 下載地址 分別下載node.lib和headers https://registry.npmmirror.com/-/binary/node/v16.17.0/win-x64/node.lib…

目標檢測算法的技術革新與應用案例

引言 目標檢測作為計算機視覺領域中的一項關鍵技術&#xff0c;近年來取得了顯著進展。從傳統的基于特征的方法到如今的深度學習算法&#xff0c;目標檢測技術在準確性、速度和魯棒性上均實現了大幅提升。本文將深入探討目標檢測算法的技術原理、發展歷程、最新進展以及實際應…

HarmonyOS--開發者證書考試地址

初級證書&#xff1a;華為開發者學堂 高級證書&#xff1a;華為開發者學堂 對應課程&#xff1a;華為開發者學堂

Linux rpm與yum

一、rpm包管理 rpm用于互聯網下載包的打包及安裝工具&#xff0c;它包含在某些Linux分發版中。它生成具有.RPM擴展名的文件。RPM是RedHat Package Manager (RedHat軟件包管理工具&#xff09;的縮寫&#xff0c;類似windows的setup.exe&#xff0c;這一文件格式名稱雖然打上了R…

辦理北京公司注銷流程和步驟說明

公司的生命周期是多變的&#xff0c;有時候&#xff0c;業務可能會結束或者出現其他原因&#xff0c;需要注銷公司。注銷公司是一個復雜的法律過程&#xff0c;需要遵循一系列的步驟和提交特定的材料。下面我們將詳細介紹北京注銷公司的流程以及需要準備的材料&#xff0c;以幫…

《等保測評實戰指南:從評估到加固的全程解析》

在當今數字化時代&#xff0c;信息安全已成為企業生存與發展的基石。隨著網絡攻擊手段的不斷演變和復雜度的提升&#xff0c;信息系統等級保護&#xff08;簡稱“等保”&#xff09;作為國家信息安全保障體系的重要組成部分&#xff0c;其重要性日益凸顯。《等保測評實戰指南&a…

私有云統一多云管理平臺主要服務內容

私有云統一多云管理平臺&#xff0c;作為企業IT架構現代化的關鍵組成部分&#xff0c;旨在為企業提供高效、靈活、安全的云計算資源管理解決方案。這類平臺通過整合和優化不同云環境(包括私有云、公有云、混合云)的管理&#xff0c;幫助企業打破云孤島&#xff0c;實現資源的統…

clickhouse-client 數據導入導出

ClickHouse提供了clickhouse-client客戶端可用于數據的快速導入導出 官方文檔&#xff1a; Inserting Data from a File JSONL 格式 導出 clickhouse-client -h 127.0.0.1 --port 9000 -u default --password XXX -d default \--query "SELECT * from default.doc_typ…

【游戲引擎之路】登神長階(五)

5月20日-6月4日&#xff1a;攻克2D物理引擎。 6月4日-6月13日&#xff1a;攻克《3D數學基礎》。 6月13日-6月20日&#xff1a;攻克《3D圖形教程》。 6月21日-6月22日&#xff1a;攻克《Raycasting游戲教程》。 6月23日-6月30日&#xff1a;攻克《Windows游戲編程大師技巧》。 …

【Qwen2部署實戰】Qwen2初體驗:用Transformers打造智能聊天機器人

系列篇章&#x1f4a5; No.文章1【Qwen部署實戰】探索Qwen-7B-Chat&#xff1a;阿里云大型語言模型的對話實踐2【Qwen2部署實戰】Qwen2初體驗&#xff1a;用Transformers打造智能聊天機器人3【Qwen2部署實戰】探索Qwen2-7B&#xff1a;通過FastApi框架實現API的部署與調用4【Q…

從任意用戶注冊到任意密碼重置

寫在最前面一句話 To be or not to be ,it‘s a question . 哎呀&#xff0c;放錯臺詞了&#xff0c;應該是 true or false , 在最近的測試中遇到了一個很有趣的點 “將 false 改為true ”就可以成功繞過驗證碼了。 T rue or false &#xff1f;&#xff1f;&#xff1f; …

Oracle PL / SQL包

在實踐中&#xff0c;您很少創建獨立的存儲函數或過程。 相反&#xff0c;你會使用一個包。 包可以一起組織相關的功能和過程&#xff0c;例如創建庫&#xff0c;但在PL / SQL中&#xff0c;庫被稱為包。 PL / SQL包有兩個部分&#xff1a; 包規格包裝體 包規范是包的公共…

使用fabric8操作k8s

文章目錄 一、引入fabric包二、認證1、使用config文件認證2、使用oauthtoken認證 三、pod的查詢和遍歷四、命名空間的創建和刪除五、deployment的創建和刪除部分參數說明1、resourceRequirements2、containerPorts3、envVarList4、volumeMounts和volumeList5、nodeAffinity 六、…

「51媒體」企業舉行新聞發布會,如何邀請媒體到場報道

傳媒如春雨&#xff0c;潤物細無聲&#xff0c;大家好&#xff0c;我是51媒體網胡老師。 媒體宣傳加速季&#xff0c;100萬補貼享不停&#xff0c;一手媒體資源&#xff0c;全國100城線下落地執行。詳情請聯系胡老師。 企業舉行新聞發布會時&#xff0c;邀請媒體到場報道是一個…