LeetCode DP篇-求子序列問題(1143、300、53)
1143. 最長公共子序列
給定兩個字串 text1 和 text2,返回這兩個字串的最長公共子序列的長度。
一個字串的 子序列 是指這樣一個新的字串:它是由原字串在不改變字元的相對順序的情況下刪除某些字元(也可以不刪除任何字元)後組成的新字串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。兩個字串的「公共子序列」是這兩個字串所共同擁有的子序列。
若這兩個字串沒有公共子序列,則返回 0。
示例 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 <= 1000
1 <= text2.length <= 1000
輸入的字串只含有小寫英文字元。
思路
//思路1:暴力,找出其中一個string的所有子序列,然後拿去第二個進行匹配,匹配到即為公告子序列 //思路2:動態規劃,將兩個String當初二維陣列的行列,從兩個String的第一個字元關係進行比較,逐漸增加字元個數,遞推進行
solution1 dp
class Solution { public int longestCommonSubsequence(String text1, String text2) { int m = text1.length(); int n = text2.length(); int[][] dp = new int[m+1][n+1]; for (int i=1; i<m+1; i++){ for (int j=1; j<n+1; j++){ if (text1.charAt(i-1) == text2.charAt(j-1)) { dp[i][j] = dp[i-1][j-1] + 1; //(i,j)=(i-1,j-1),因此從1開始遍歷到m+1 }else{ dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]); } } } return dp[m][n]; } }
dp2
//通過將字串轉化為char陣列提升效能
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
char[] chars1 = text1.toCharArray();
char[] chars2 = text2.toCharArray();
int m = text1.length();
int n = text2.length();
int[][] dp = new int[m+1][n+1];
for(int i = 1; i < m+1; i++){
for(int j = 1; j < n+1; j++){
if (chars1[i-1] == chars2[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];
}
}
最長公共子串
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
char[] chars1 = text1.toCharArray();
char[] chars2 = text2.toCharArray();
int m = text1.length();
int n = text2.length();
int[][] dp = new int[m+1][n+1];
int max = 0;
for(int i = 1; i < m+1; i++){
for(int j = 1; j < n+1; j++){
if (chars1[i-1] == chars2[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
max = Math.max(max,dp[i][j]);
}
}
}
return max;
}
}
300. 最長上升子序列
給定一個無序的整數陣列,找到其中最長上升子序列的長度。
示例:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
說明:
可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。
你演算法的時間複雜度應該為 O(n2) 。
進階: 你能將演算法的時間複雜度降低到 O(n log n) 嗎?
solution1 普通動態規劃
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp,1);
for (int i = 0; i < nums.length; i++){
for (int j = 0; j < i; j++){
if(nums[j]<nums[i]) dp[i] = Math.max(dp[i],dp[j]+1);
}
}
int res = 0;
for(int i = 0; i < nums.length; i++){
res = Math.max(res,dp[i]);
}
return res;
}
}
//普通dp
//1.dp陣列
//2.求base case
//2.數學歸納法求dp[i],即確定狀態轉移方程
solution2 優化dp
class Solution {
public int lengthOfLIS(int[] nums) {
int[] top = new int[nums.length];
int piles = 0;
for (int i = 0; i < nums.length; i++){
int left = 0, right = piles;
int curr = nums[i];
//二分查詢,查詢小於當前且最大的數
while(left < right){
int mid = (left + right) >> 1;
if (top[mid] > curr){
right = mid;
}else if(top[mid] < curr){
left = mid + 1;
}else{
right = mid;
}
}
//牌堆最大值小於當前,新建堆
if (left == piles) piles ++;
top[left] = curr;
}
return piles;
}
}
//優化dp
//在普通dp狀態方程的求解上,利用二分查詢,將時間複雜度縮小到 O(log N)
//參考資料:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-she-ji-fang-fa-zhi-pai-you-xi-jia/
53. 最大子序和
給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4]
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。
進階:
如果你已經實現複雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。
class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
for(int i = 1; i < nums.length; i++){
dp[i] = Math.max(nums[i],nums[i]+dp[i-1]);
}
int res = Integer.MIN_VALUE;
for(int j = 0; j < nums.length; j++){
res = Math.max(dp[j],res);
}
return res;
}
}
//!!!考慮每一數加不加入之前還是另起陣列
//1、dp陣列,每一狀態儲存有當前數加入的最大和子串
//2、dp[0] = nums[0]
//3、狀態轉移:max(nums[i],nums[i]+dp[i-1])