动态规划_之_子序列问题

本文探讨了如何使用动态规划解决三个经典序列问题:最长公共子序列、最长递增子序列和最长回文子序列。针对每个问题,分别给出了详细的解题思路,包括状态转移方程的建立和优化的动态规划实现。通过这些实例,读者可以深入理解动态规划在序列问题中的应用。
摘要由CSDN通过智能技术生成

最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

解:

设置子序列的问题==》十有八九是动态规划

明确dp数组

dp[i][j]表示text1的前i个元素和text2的前j个元素的最长公共子序列长度

找dp数组的表示–状态转移方程

//将dp[i][j]表示出来
//若text1[i]==text2[j],dp[i][j]=dp[i-1][j-1]+1
//不等于dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

base case

dp[0][.]和dp[.][0]均是0

搞定!

//递归解法,由上而下:
 public int longestCommonSubsequence(String text1, String text2) {
int m=text1.length()-1;
int n=text2.length()-1;
  char[] tt1=text1.toCharArray();
 char[] tt2=text2.toCharArray();
return dp(m,n,tt1,tt2);
 }
 int dp(int i,int j,char[] tt1,char[] tt2){
   if(i==-1||j==-1)return 0;
   if(tt1[i]==tt2[j]){
     return dp(i-1,j-1,tt1,tt2)+1;
    }else{
       return Math.max(dp(i-1,j,tt1,tt2),dp(i,j-1,tt1,tt2));
    }
  }

用dp数组优化-

//由底向上
public int longestCommonSubsequence(String text1, String text2) {
	// 字符串转为char数组以加快访问速度
	char[] str1 = text1.toCharArray();
	char[] str2 = text2.toCharArray();

	int m = str1.length, n = str2.length;
	// 构建dp table,初始值默认为0
	int[][] dp = new int[m + 1][n + 1];
	// 状态转移
	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++)
			if (str1[i - 1] == str2[j - 1])
				// 找到LCS中的字符
				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];
}

最长自增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

解:

1.首先,要去定义dp数组,明确dp数组的含义。本题中,dp[i]表示以nums[i]结尾的最长递增子序列的长度。
2.假设dp[0]…dp[i-1]已知,怎么表示dp[i]?

for(int j=0;j<i;j++){
if(nums[j]<nums[i]){
	dp[i]=Math.max(dp[i],dp[j]+1);
}
}

3.将dp[0]…dp[i]都表示出来。

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);
		}
	}
}

4.base case就是dp[i]均是1.

  public int lengthOfLIS(int[] nums) {
//dp[i]表示以num[i]结尾的递增组序列的长度

    int[] dp=new int[nums.length];
    for(int i=0;i<dp.length;i++){
        dp[i]=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[j]+1,dp[i]);
            }
           
        }
    }
    int res=0;
    for(int i=0;i<dp.length;i++){
        res=Math.max(dp[i],res);
    }
    return res;
    }

最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

解:

class Solution {
    public int longestPalindromeSubseq(String s) {
         int n = s.length();
        int[][] f = new int[n][n];
        for(int i=0;i<n;i++){
            f[i][i]=1;
        }
        for (int i = n-1; i>=0 ; i--) {
            for (int j = i + 1; j < n; j++) {
                if (s.charAt(i) == s.charAt(j)) {
                    f[i][j] = f[i + 1][j - 1] + 2;
                } else {
                    f[i][j] = Math.max(f[i + 1][j], f[i][j - 1]);
                }
            }
        }
        return f[0][n - 1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值