代码随想录——动态规划(三):子序列系列

题目来自《代码随想录》

文章目录

    • 300. 最长递增子序列
    • 674. 最长连续递增序列
    • 718. 最长重复子数组
    • 1143. 最长公共子序列
    • 1035. 不相交的线
    • 53. 最大子数组和
    • 392. 判断子序列
    • 115. 不同的子序列
    • 583. 两个字符串的删除操作
    • 72. 编辑距离
    • 647. 回文子串
    • 516. 最长回文子序列

300. 最长递增子序列

https://leetcode-cn.com/problems/longest-increasing-subsequence/
注意,本题未要求连续

	/*
	 * 定义 dp[i] 为考虑前 i个元素,以第 i个数字结尾的最长上升子序列的长度,
	 * 注意 nums[i] 必须被选取。
	 */
	public int lengthOfLIS(int[] nums) {
		int len = nums.length;
		if(len <= 1) return len;
		
		int[] dp = new int[len];
		for(int i=0; i<len; i++) dp[i] = 1;//初始化
		
		int maxans = 1;

		for(int i=1; i<len; i++){
			for(int j=0; j<i; j++){
				if(nums[i] > nums[j]) {
					dp[i] = Math.max(dp[i], dp[j]+1);
				}
			}
			maxans = Math.max(maxans, dp[i]);
			show(dp);
		}
		
		return maxans;
	}
	
	public void show(int[] num){
		for(int i:num) System.out.print(i + " ");
		System.out.print("\n");
	}
	
	
	
	//注意不是连续!!错误!!
	public int lengthOfLIS2(int[] nums) {
		int len = nums.length;
		if(len <= 1) return len;
		
		int l = 0;
		int r = 1;
		int cur = 1;
		int res = 0;
		while(r<len){
			if(nums[r]>nums[r-1]){//严格递增
				System.out.println(nums[r] + ">" + nums[r-1] + ", 当前递增");
				System.out.println("l=" + l + ", r=" + r);
				cur++;
				r++;
				res = Math.max(cur, res);
			}else{
				System.out.println("l=" + l + ", r=" + r);
				l = r;
				r++;
				cur = 0;
			}
		}
		
		return res;
	}

674. 最长连续递增序列

https://leetcode-cn.com/problems/longest-continuous-increasing-subsequence/

	/*
	 * 2. 动态规划
	 * 执行用时:1 ms, 在所有 Java 提交中击败了99.77%的用户
	 * 内存消耗:42.2 MB, 在所有 Java 提交中击败了6.06%的用户
	 */
	public int findLengthOfLCIS2(int[] nums) {
		int len = nums.length;
		if(len <= 1) return len;
		
		int[] dp = new int[len];
		dp[0] = 1;
		
		int res = 1;
		for(int i=1; i<len; i++){
			if(nums[i] > nums[i-1]){
				dp[i] = dp[i-1]+1;
				res = Math.max(res, dp[i]);
			}else{
				dp[i] = 1;
			}
		}
		
		return res;
	}
	
	/*
	 * 1. 双指针
	 * 执行用时:1 ms, 在所有 Java 提交中击败了99.77%的用户
	 * 内存消耗:42.1 MB, 在所有 Java 提交中击败了12.63%的用户
	 * 
	 * 注意:
	 * 1. 一开始res要赋值为0
	 * 2. 一旦不递增,cur不归零,归1
	 */
	public int findLengthOfLCIS(int[] nums) {
		int len = nums.length;
		if(len <= 1) return len;
		
		int l = 0;
		int r = 1;
		int cur = 1;
		int res = 1;
		while(r<len){
			if(nums[r]>nums[r-1]){//严格递增
				System.out.print(nums[r] + ">" + nums[r-1] + ", 当前递增");
				//System.out.println("l=" + l + ", r=" + r);
				cur++;
				r++;
				res = Math.max(cur, res);	
				System.out.println(",当前cur:" + cur);
			}else{
				System.out.print(nums[r] + "<=" + nums[r-1] + ", 当前非递增");
				//System.out.println("l=" + l + ", r=" + r);
				l = r;
				r++;
				cur = 1;
				System.out.println(",当前cur:" + cur);
			}
		}
		
		return res;
	}

718. 最长重复子数组

https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/

	/*
	 * dp[i][j]:以下标i结尾的nums1和以下标j结尾的nums2的最长公共子序列为dp[i][j]
	 */
	public int findLength(int[] nums1, int[] nums2) {
		int len1 = nums1.length;
		int len2 = nums2.length;
		if(len1 == 0 || len2 == 0) return 0;
		
		int[][] dp = new int[len1+1][len2+1];
		
		int res = 0;
		
		for(int i=0; i<len1+1; i++){
			for(int j=0; j<len2+1; j++){
				if(i==0 || j==0) {//左边上边都是0
					dp[i][j] = 0;continue;
				}
				
				if(nums2[j-1] == nums1[i-1]){
					dp[i][j] = dp[i-1][j-1] + 1;
					res = Math.max(res, dp[i][j]);
				}
				show(dp);
			}
		}
		
		return res;
	}

	
	public void show(int[][] num){
		for(int i=0; i<num.length; i++){
			for(int j=0; j<num[0].length; j++){
				System.out.print(num[i][j] + " ");
			}
			System.out.print("\n");
		}
		System.out.println("*************");
	}

1143. 最长公共子序列

https://leetcode-cn.com/problems/longest-common-subsequence/

	/*
	 * dp[i][j]:长度为[0, i-1]的字符串text1与长度为[0, j-1]的字符串text2
	 * 			的最长公共子序列为dp[i][j]
	 */
	public int longestCommonSubsequence(String text1, String text2) {
		int len1 = text1.length();
		int len2 = text2.length();
		if(len1 == 0 || len2 == 0) return 0;
		
		int[][] dp = new int[len1+1][len2+1];
		int res = 0;
		
		for(int i=0; i<=len1; i++) {
			for(int j=0; j<=len2; j++) {
				if(i==0 || j==0) {//左边上边都是0
					dp[i][j] = 0;continue;
				}
				/*
				 * dp[i][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]);
				}
				res = Math.max(dp[i][j], res);
			}
		}
		
		return res;
	}

1035. 不相交的线

https://leetcode-cn.com/problems/uncrossed-lines/
其实就是最长公共子序列。

	/*
	 * 直线不能相交,这就是说明在字符串A中 找到一个与字符串B相同的子序列,
	 * 且这个子序列不能改变相对顺序,只要相对顺序不改变,链接相同数字的直线就不会相交。
	 */
	public int maxUncrossedLines(int[] nums1, int[] nums2) {
		int len1 = nums1.length;
		int len2 = nums2.length;
		if(len1 == 0 || len2 == 0) return 0;
		
		int[][] dp = new int[len1+1][len2+1];
		int res = 0;
		
		for(int i=0; i<=len1; i++) {
			for(int j=0; j<=len2; j++) {
				if(i==0 || j==0) {//左边上边都是0
					dp[i][j] = 0;continue;
				}
				/*
				 * dp[i][j]来自三个方向:左上,左,上
				 */
				if(nums1[i-1] == nums2[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]);
				}
				res = Math.max(dp[i][j], res);
			}
		}
		
		return res;
	}

53. 最大子数组和

  1. 注意贪心的顺序
  2. 注意动规初始化
	/*
	 * 贪心
	 */
	public int maxSubArray(int[] nums) {
		int len = nums.length;
		if(len == 0) return 0;
		
		int cur = 0;
		int res = Integer.MIN_VALUE; 
		
		for(int i=0; i<len; i++){
			cur += nums[i];
			if(cur > res) res = cur;
			if(cur < 0) cur = 0;
		}
		
		return res;
	}
	
	/*
	 * 动规
	 */
	public int maxSubArray1(int[] nums) {
		int len = nums.length;
		if(len == 0) return 0;
		
		int[] dp = new int[len];
		dp[0] = nums[0];
		int res = nums[0];
		
		for(int i=1; i<len; i++){
			/*
			 * 两个来源,两个求max
			 * 1. 从自己这重新开始
			 * 2. 加入前一个
			 */
			dp[i] = Math.max(nums[i], dp[i-1]+nums[i]);
			res = Math.max(res, dp[i]);
			show(dp);
		}
		
		return res;
	}

392. 判断子序列

https://leetcode-cn.com/problems/is-subsequence/submissions/
这个题要好好再和别的对比一下

  1. 相等的时候左上角+1
  2. 不等的时候就是左边
  3. 没有取max的情况
	public boolean isSubsequence(String s, String t) {
		int len1 = s.length();
		int len2 = t.length();
		if(len1 == 0) return true;
		if(len2 == 0 || len1>len2) return false;
		
		int[][] dp = new int[len1+1][len2+1];
		
		for(int i=1; i<=len1; i++){
			for(int j=1; j<=len2; j++){
				if(s.charAt(i-1) == t.charAt(j-1)){
					dp[i][j] = dp[i-1][j-1]+1;
				}else{
					dp[i][j] = dp[i][j-1];
				}
//				show(dp);
			}
		}
		
		return dp[len1][len2] == len1;
	}

115. 不同的子序列

https://leetcode-cn.com/problems/distinct-subsequences/

	/*
	 * 输入:s = "rabbbit", t = "rabbit"
	 * 输出:3
	 * dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
	 */
	public int numDistinct(String s, String t) {
		int len1 = s.length(); int len2 = t.length();
		if(len2 == 0) return 0;
		
		int[][] dp = new int[len1+1][len2+1];
		dp[0][0] = 1;
		
		for(int i=1; i<=len1; i++){
			for(int j=0; j<=len2; j++){
				if(j == 0){
					dp[i][j] = 1;continue;//初始化
				}
				
				if(s.charAt(i-1) == t.charAt(j-1)){//相等由两部分构成
					dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
				}else{
					dp[i][j] = dp[i-1][j];
				}
			}
		}
		
		return dp[len1][len2];
		
		
		/*
		 * 1. s[i - 1] 与 t[j - 1]相等
		 * 		一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。
		 * 		一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。
		 * 2. s[i - 1] 与 t[j - 1] 不相等
		 * 		dp[i][j]只有一部分组成,不用s[i - 1]来匹配,即:dp[i - 1][j]
		 */
	}

583. 两个字符串的删除操作

https://leetcode-cn.com/problems/delete-operation-for-two-strings/
min的括号不要标错位置!

	/*
	 * word1 = "sea", word2 = "eat",输出:2
	 * word1 = "leetcode", word2 = "etco",输出:4
	 */
	public int minDistance(String word1, String word2) {
		int len1 = word1.length();
		int len2 = word2.length();
		if(len1 == 0) return len2;
		if(len2 == 0) return len1;
		
		int[][] dp = new int[len1+1][len2+1];
        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.charAt(i-1) == word2.charAt(j-1)){
					//相等的时候就是左上角
					dp[i][j] = dp[i-1][j-1];
				}else{
					//不等的时候三种情况,1减一个,2减一个,一起减一个,三个求min
					dp[i][j] = Math.min(dp[i-1][j-1]+2, Math.min(dp[i][j-1]+1, dp[i-1][j]+1));
					
//					if(i == 2 && j == 2){
//						System.out.println("i = " + i + ", j = " + j);
//						System.out.println("dp[i-1][j-1]+2 = " + (dp[i-1][j-1]+2));
//						System.out.println("dp[i][j-1]+1 = " + (dp[i][j-1]+1));
//						System.out.println("dp[i-1][j]+1 = " + (dp[i-1][j]+1));
//						System.out.println((dp[i-1][j]+1) + "和" + (dp[i][j-1]+1) + 
//											"的最小值为:" + Math.min(dp[i][j-1]+1, dp[i-1][j]+1));
//						System.out.println("最后dp[i][j] = " + (dp[i][j]));
//					}
					
				}
			}
			show(dp);
		}
		
		return dp[len1][len2];
	}

72. 编辑距离

https://leetcode-cn.com/problems/edit-distance/

	public int minDistance(String word1, String word2) {
		int len1 = word1.length();
		int len2 = word2.length();
		if(len1 == 0) return len2;
		if(len2 == 0) return len1;
		
		int[][] dp = new int[len1+1][len2+1];
        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++){
				//1.当前比较字符相同,直接等于左上角
				if(word1.charAt(i-1) == word2.charAt(j-1)){
					dp[i][j] = dp[i-1][j-1];
				}else{
					/*
					 * 1. 增或删
					 * 		①word1删,dp[i - 1][j] + 1
					 * 		①word2删,dp[i][j - 1] + 1
					 * 2. 替换
					 * 		dp[i - 1][j - 1] + 1
					 */								
					dp[i][j] = Math.min(dp[i-1][j-1], 
							Math.min(dp[i][j-1], dp[i-1][j])) + 1;
				}
			}
		}
		return dp[len1][len2];
	}

647. 回文子串

  1. dp含义
  2. 状态转移方程
  3. 遍历顺序
	public int countSubstrings(String s) {
		int len = s.length();
		if(len == 0) return 0;
		
		boolean[][] dp = new boolean[len][len];
		int res = 0;
		for(int i=len-1; i>=0; i--){
			for(int j=i; j<len; j++){
				
				if(s.charAt(i) == s.charAt(j)){
					if(i == j || j-i == 1 || dp[i+1][j-1] == true){
						dp[i][j] = true;
						res++;
					}
				}			
			}
			show(dp);
		}		
		return res;
	}

516. 最长回文子序列

https://leetcode-cn.com/problems/longest-palindromic-subsequence/

	public int longestPalindromeSubseq(String s) {
		int len = s.length();
		if(len == 0) return 0;
		
		int[][] dp = new int[len][len];
		for(int i=len-1; i>=0; i--){
			for(int j=i; j<len; j++){
				if(i == j){//对角线都是1
					dp[i][j] = 1; continue;
				}
				
				if(s.charAt(i) == s.charAt(j)){//相等时,左下角加2
					dp[i][j] = dp[i+1][j-1] + 2;
				}else{//不等时,左边或者下边的max
					dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);
				}
			}
		}		
		return dp[0][len-1];
	}

你可能感兴趣的:(数据结构与算法,算法,java)