两个数组的dp问题

目录

最长公共子序列

不相交的线

不同的子序列

通配符匹配

正则表达式匹配

交错字符串

两个字符串的最小ASCII删除和

最长重复子数组


声明:接下来主要使用动态规划来解决问题!!!

最长公共子序列

题目

思路

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

状态表示:dp[i][j]表示字符串1的[0,i]区间和字符串2的[0,j]区间内的所有公共子序列的最长的长度.

状态转移方程:if(s1[i]==s2[j])     dp[i][j]=dp[i-1][j-1]+1;

                         else     dp[i][j]=max(dp[i-1][j],dp[i][j-1]).

初始化:不用初始化。

填表顺序:从左往右。

返回值:dp[m][n].

代码

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m=text1.size(),n=text2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++){
                if(text1[i-1]==text2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
                else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        return dp[m][n];
    }
};





//或者下面的




class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m=text1.size(),n=text2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        text1=' '+text1;
        text2=' '+text2;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++){
                if(text1[i]==text2[j]) dp[i][j]=dp[i-1][j-1]+1;
                else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        return dp[m][n];
    }
};
不相交的线

题目

思路

以例2为例,

通过观察不难发现,实则是找两个数组的最长公共子数组,与上一道题最长公共子序列如出一辙。

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

状态表示:dp[i][j]表示数组1的[0,i]区间和数组2的[0,j]区间内的所有公共子数组的最长的长度.

状态转移方程:if(nums1[i]==s2[j])     dp[i][j]=dp[i-1][j-1]+1;

                         else     dp[i][j]=max(dp[i-1][j],dp[i][j-1]).

初始化:不用初始化。

填表顺序:从左往右。

返回值:dp[m][n].

代码

class Solution {
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
        int m=nums1.size(),n=nums2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++){
                if(nums1[i-1]==nums2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
                else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        return dp[m][n];
    }
};
不同的子序列

题目

思路

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

状态表示:dp[i][j]表示s[0,i]区间内有多少种t[0,j]区间字符串。

状态转移方程:dp[i][j]=dp[i-1][j]+dp[i-1][j-1]【后者必须满足s[i]==t[j]】

初始化:dp[i][0]=1,第一列全都初始化为1.

返回值:dp[m][n].

代码

class Solution {
public:
    int numDistinct(string s, string t) {
        int m=s.size(),n=t.size();
        vector<vector<double>> dp(m+1,vector<double>(n+1));
        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++){
                dp[i][j]=dp[i-1][j];
                if(s[i-1]==t[j-1])
                    dp[i][j]+=dp[i-1][j-1];
            }
        return dp[m][n];
    }
};




//或者下面的代码




class Solution {
public:
    int numDistinct(string s, string t) {
        int m=s.size(),n=t.size();
        s=' '+s;
        t=' '+t;
        vector<vector<double>> dp(m+1,vector<double>(n+1));
        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++){
                dp[i][j]=dp[i-1][j];
                if(s[i]==t[j])
                    dp[i][j]+=dp[i-1][j-1];
            }
        return dp[m][n];
    }
};
通配符匹配

题目

思路

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

状态表示:dp[i][j]表示字符串p[0,j]区间字符串是否能匹配字符串s[0,i]区间字符串。

针对p[j]=='*',化简

【1】数学方法

【2】分析法

可以先让'*’匹配一个s字符串末尾字符,但是不消掉'*'

状态转移方程:

                if(p[j-1]!='*') dp[i][j]=(p[j-1]=='?' || p[j-1]==s[i-1]) && dp[i-1][j-1];

                else dp[i][j]=dp[i-1][j] || dp[i][j-1];

初始化:

第一列除去[0,0]位置之外全都为fasle,

第一行除去[0,0]位置之外,初始化规则:当字符串p[0,j]区间含不为'*'字符,后边都为false,否则为true.

填表顺序:从上往下,从左往右。

返回值:dp[m][n].

代码

class Solution {
public:
    bool isMatch(string s, string p) {
        int m=s.size(),n=p.size();
        vector<vector<bool>> dp(m+1,vector<bool>(n+1));
        dp[0][0]=true;
        for(int i=1;i<=n;i++){
            if(p[i-1]=='*') dp[0][i]=true;
            else break;
        }
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++){
                if(p[j-1]!='*') dp[i][j]=(p[j-1]=='?' || p[j-1]==s[i-1]) && dp[i-1][j-1];
                else dp[i][j]=dp[i-1][j] || dp[i][j-1];
            }
        return dp[m][n];
    }
};



//或者为下面的代码



class Solution {
public:
    bool isMatch(string s, string p) {
        int m=s.size(),n=p.size();
        vector<vector<bool>> dp(m+1,vector<bool>(n+1));
        // dp[0][0]=true;
        for(int i=0;i<=n;i++){
            bool flag=true;
            for(int j=0;j<i;j++){
                if(p[j]!='*') flag=false;
            }
            dp[0][i]=flag;
        }
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++){
                if(p[j-1]==s[i-1]) dp[i][j]=dp[i-1][j-1];
                else if(p[j-1]=='?') dp[i][j]=dp[i-1][j-1];
                else if(p[j-1]=='*') dp[i][j]=dp[i-1][j] || dp[i][j-1];
            }
        return dp[m][n];
    }
};
正则表达式匹配

题目

思路

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

状态表示:dp[i][j]表示字符串p[0,j]区间字符串是否能匹配字符串s[0,i]区间字符串。

针对p[j]=='*',化简

【1】数学方法

【2】分析法

可以先让'*’和前一个字符匹配一个s字符串末尾字符,但是不消掉'*'和前一个字符

状态转移方程:

                if(p[j-1]=='*') dp[i][j]=((p[j-2]==s[i-1] || p[j-2]=='.') && dp[i-1][j]) || dp[i][j-2];

                else dp[i][j]=(p[j-1]=='.' || p[j-1]==s[i-1]) && dp[i-1][j-1];

初始化:

第一列除去[0,0]位置之外全都为fasle,

第一行除去[0,0]位置之外,初始化规则:当字符串p[0,j]区间偶数位置都为'*'字符,则为false,其余位置为false,否则之后都为true.

填表顺序:从上往下,从左往右。

返回值:dp[m][n].

代码

class Solution {
public:
    bool isMatch(string s, string p) {
        int m=s.size(),n=p.size();
        vector<vector<bool>> dp(m+1,vector<bool>(n+1));
        dp[0][0]=true;
        for(int i=2;i<=n;i+=2){
            if(p[i-1]=='*') dp[0][i]=true;
            else break;
        }
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(p[j-1]=='*') dp[i][j]=((p[j-2]==s[i-1] || p[j-2]=='.') && dp[i-1][j]) || dp[i][j-2];
                else dp[i][j]=(p[j-1]=='.' || p[j-1]==s[i-1]) && dp[i-1][j-1];
            }
        }
        return dp[m][n];
    }
};




//或者为下面的代码




class Solution {
public:
    bool isMatch(string s, string p) {
        int m=s.size(),n=p.size();
        s=' '+s;
        p=' '+p;
        vector<vector<bool>> dp(m+1,vector<bool>(n+1));
        dp[0][0]=true;
        for(int i=2;i<=n;i+=2){
            if(p[i]=='*') dp[0][i]=true;
            else break;
        }
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(p[j]=='*') dp[i][j]=((p[j-1]==s[i] || p[j-1]=='.') && dp[i-1][j]) || dp[i][j-2];
                else dp[i][j]=(p[j]=='.' || p[j]==s[i]) && dp[i-1][j-1];
            }
        }
        return dp[m][n];
    }
};
交错字符串

题目

思路

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

为了便于表述,在s1,s2,s3每个字符串的最前边增加一个空格。

状态表示:dp[i][j]表示字符串s1[1,i]区间和字符串s2[1,j]区间能否凑成字符串s3的[1,i+j]区间。

状态转移方程:

初始化:

初始化第一行规则:依次判断字符串s2和s3对应位置是否相等,相等则为true,否则为false并break.

初始化第一列规则:依次判断字符串s1和s3对应位置是否相等,相等则为true,否则为false并break.

填表顺序:从上往下,从左往右。

返回值:dp[m][n]。

代码

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int m=s1.size(),n=s2.size();
        if(m+n!=s3.size()) return false;
        s1=' '+s1;
        s2=' '+s2;
        s3=' '+s3;
        vector<vector<bool>> dp(m+1,vector<bool>(n+1));
        for(int i=0;i<=m;i++)
            if(s1[i]==s3[i]) dp[i][0]=true;
            else break;
        for(int i=0;i<=n;i++)
            if(s2[i]==s3[i]) dp[0][i]=true;
            else break;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                dp[i][j]=(s1[i]==s3[i+j] && dp[i-1][j]) || (s2[j]==s3[i+j] && dp[i][j-1]);
            }
        }
        return dp[m][n];
    }
};
两个字符串的最小ASCII删除和

题目

思路

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

题目中是求两个字符串的最小ASCII删除和,若直接求两个字符串的最小ASCII删除和有点麻烦,采用“正难则反”的思想,由于字符串时确定的,即两个字符串的ASCII值总和是确定的,那么只需要求出两个字符串的所有公共子序列中ASCII值最大的子序列,用sum-2*公共子序列ASCII最大值即可。

状态表示:dp[i][j]表示字符串s1[0,i]区间和字符串s2[0,j]区间所有的公共子序列中ASCII总和最大的值。

状态转移方程:

                dp[i][j]=max(dp[i][j-1],dp[i-1][j]);

                if(s1[i-1]==s2[j-1])

                    dp[i][j]=max(dp[i][j],dp[i-1][j-1]+s1[i-1]);

初始化:不用初始化。

填表顺序:从上往下,从左往右。

返回值:ret-2*dp[m][n]。

代码

class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        int m=s1.size(),n=s2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        int ret=0;
        for(char ch:s1)
            ret+=ch;
        for(char ch:s2)
            ret+=ch;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
                if(s1[i-1]==s2[j-1]) 
                    dp[i][j]=max(dp[i][j],dp[i-1][j-1]+s1[i-1]);
            }
        }
        return ret-2*dp[m][n];
    }
};
最长重复子数组

题目

思路

根据经验+题目要求,我们将屡试不爽的采用“以某个位置”为结尾来分析问题。

如果以区间来分析问题,仔细分析一下会发现是错误的,因为子数组是连续的,而子序列可以连续也可以不连续,那么我们将以某个位置为结尾来分析问题。

状态表示:dp[i][j]表示数组nums1[0,i]和数组nums2[0,j]区间的所有重复子数组中的最长的长度。

状态转移方程:

初始化:不用初始化。

填表顺序;从上往下,从左往右。

返回值:dp表最大值。

代码

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int m=nums1.size(),n=nums2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        int ret=0;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(nums1[i-1]==nums2[j-1]) 
                    dp[i][j]=dp[i-1][j-1]+1,ret=max(ret,dp[i][j]);
            }
        }
        return ret;
    }
};

好的,我理解你的问题是要求将一个整数数组分成两个数组,使得这两个数组的元素之和相等,对吧? 这是一个经典的问题,被称为“数组分割问题”(Array Partition Problem)。这个问题可以用动态规划(Dynamic Programming)算法来解决。 具体来说,我们可以先计算出整个数组的元素之和 sum,然后尝试在数组中选择一些元素,使得它们的元素之和为 sum/2。如果能找到这样的一组元素,那么剩下的元素就组成另一个子数组,这两个数组的元素之和就相等了。 为了实现这个算法,我们可以用一个二维的布尔型数组 dp,其中 dp[i][j] 表示在前 i 个元素中是否存在一些元素的元素之和为 j。具体的状态转移方程如下: dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]] 其中 nums 是原数组dp[i][j] 表示选或不选第 i 个元素。 最终,如果存在 dp[n][sum/2] 为 true,那么就可以分成两个数组,否则就不能。 以下是这个算法的 Python 代码实现: ```python def can_partition(nums): n = len(nums) total_sum = sum(nums) if total_sum % 2 != 0: return False target_sum = total_sum // 2 dp = [[False] * (target_sum + 1) for _ in range(n)] for i in range(n): dp[i][0] = True for j in range(1, target_sum + 1): if nums[0] == j: dp[0][j] = True for i in range(1, n): for j in range(1, target_sum + 1): if j < nums[i]: dp[i][j] = dp[i-1][j] else: dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]] return dp[n-1][target_sum] ``` 希望对你有所帮助!
评论 99
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新绿MEHO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值