動態規劃類整理
1.最大子序和
給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。
動態規劃,狀態轉移方程:dp[i] = max{A[i], dp[i-1]+A[i]}
public static int maxSubArray(int[] nums) { if (nums == null) return 0; int len = nums.length; int max = nums[0]; int[] dp = new int[len]; dp[0] = nums[0]; for (int i = 1; i < len; i++) { if (dp[i - 1] > 0) dp[i] = dp[i - 1] + nums[i]; else dp[i] = nums[i]; } for (int i = 0; i < len; i++) { if (max < dp[i]) max = dp[i]; } return max; }
不借助dp陣列:
一定先判斷 if(max<sum)後 if(sum<0);
因為如果全負數的情況,sum=0;會影響導致結果為0;
public int maxSubArray(int[] nums) { if (nums == null) return 0; int len = nums.length; int max = nums[0]; int sum = 0; for(int i=0;i<len;i++) { sum=sum+nums[i]; if(max<sum) max=sum; if(sum<0) sum=0; } return max; }
2.爬樓梯
假設你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是一個正整數。
示例 1:
輸入: 2
輸出: 2
解釋: 有兩種方法可以爬到樓頂。
1 階 + 1 階
2 階
思路:跳臺階問題
public int climbStairs(int n) { if (n <= 0) return 0; if (n < 3) return n; int f1 = 1, f2 = 2, res = 0; for (int i = 3; i <= n; i++) { res = f1 + f2; f1 = f2; f2 = res; } return res; }
300.最長上升子序列
給定一個無序的整數陣列,找到其中最長上升子序列的長度。
示例:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
思路:動態規劃dp[i] 儲存的是以i為最後元素的,最長子序列長度;
因此需要內迴圈j從0遍歷到i,如果存在比nums[i]小的元素,則計算dp轉移方程;
dp[i]= max(dp[i], dp[j] + 1);
注意:此類題是O(N2),上升子序列不是連續的;
public int lengthOfLIS(int[] nums) {
if(nums==null)
return 0;
int len = nums.length;
int[] dp=new int[len];
for(int i=0;i<len;i++){
dp[i]=1;
}
int res = 0;
for(int i=0;i<len;i++) {
for(int j=0;j<i;j++) {
if(nums[i]>nums[j])
dp[i]=dp[i]>dp[j]+1?dp[i]:dp[j]+1;
}
res=res>dp[i]?res:dp[i];
}
return res;
}
674.最長連續遞增序列
給定一個未經排序的整數陣列,找到最長且連續的的遞增序列。
示例 1:
輸入: [1,3,5,4,7]
輸出: 3
解釋: 最長連續遞增序列是 [1,3,5], 長度為3。
儘管 [1,3,5,7] 也是升序的子序列, 但它不是連續的,因為5和7在原數組裡被4隔開。
思路:設計數器count,如果遇到更大的數,則加一;否則,計數器歸一;
res始終儲存當前最大count值;
public static int findLengthOfLCIS(int[] nums) {
if(nums==null)
return 0;
int res=1;
int len = nums.length;
int cur=nums[0];
int count=1;
for(int i=1;i<len;i++) {
if(nums[i]>cur)
count++;
else
count=1;
res=res>count?res:count;
cur=nums[i];
}
return res;
}
對於兩個字串,請設計一個高效演算法,求他們的最長公共子序列的長度,這裡的最長公共子序列定義為有兩個序列U1,U2,U3…Un和V1,V2,V3…Vn,其中Ui<Ui+1,Vi<Vi+1。且A[Ui] == B[Vi]。
給定兩個字串A和B,同時給定兩個串的長度n和m,請返回最長公共子序列的長度。保證兩串長度均小於等於300。
測試樣例:
“1A2C3D4B56”,10,“B1D23CA45B6A”,12
返回:6
思路:
動態規劃;dp[i][j]表示A字串的前i字元與B串的前j字元的最長公共子序列的長度;
轉移方程:當A[i]==B[j],dp[i][j]=dp[i-1][j-1]+1;
否則dp[i][j]=max{dp[i-1][j],dp[i][j-1]};
注意:子序列問題同子串問題區分;子串是連續的;
public static int findLCS(String A, int n, String B, int m) {
int[][] dp = new int[n + 1][m + 1];
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (A.charAt(i - 1) == B.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[n][m];
}
最長公共連續子串
輸入
abcde
abgde
輸出
2
思路:與上題類似,動態規劃思想;
主要區別是,dp矩陣意義為A串以i結尾與B串以j結尾的最長公共連續子串;
當A[i-1] = B[j-1]時,dp[i][j] = dp[i -1][j -1] + 1;
A[i-1] != B[j-1]時,以他們結尾的公共子串長度必然為0;
最後從dp中取出長度最大的值即為最長公共子串;
public static int findLongest(String A, int n, String B, int m) {
int [][]dp=new int[n+1][m+1];
int max=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if (A.charAt(i - 1) == B.charAt(j - 1))
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j]=0;
max=Math.max(max, dp[i][j]);
}
}
return max;
}