代码随想录:动态规划

343.整数拆分

代码如下:

class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n+1);
        dp[2]=1;
        for(int i=3; i<=n; i++){
            for(int j=1; j<i; j++){
                //因为dp[i]是在for循环内,每次都会产生新的值,最后取最大的dp[i];
                dp[i]=max(dp[i],max((i-j)*j,dp[i-j]*j));
            }
        }
        return dp[n];
    }
};

为什么是dp[i-j]*j,而不是dp[i-j]*dp[j],因为是for循环你进行遍历,j从1开始,所以就不必使用dp[j],同时注意在取最大值时,应该加上dp[i],与他们的比较,因为dp[i]也是在for循环内,每次都会更新,所以最后要取最大值。

96.不同的二叉搜索树(有意思)

代码如下:

class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n+1);
        dp[0]=1;
        for(int i=1; i<=n; i++){
            for(int j=1; j<=i; j++){
                dp[i]+=dp[j-1]*dp[i-j];
            }
        }
        return dp[n];
    }
};

二维dp数组0-1背包问题

代码如下:

#include <iostream>
#include <vector>
using namespace std;

void fun(){
    vector<int> weight{1,3,4};
    vector<int> value{15,20,30};
    int bagweight=4;

    //初始化dp数组
    vector<vector<int>> dp(weight.size(),vector<int>(bagweight+1,0));

    //dp数组赋值 第一行赋值
    for(int j=weight[0]; j<=bagweight; j++){
        dp[0][j]=value[0];
    }

    //遍历
    //先遍历物品 再遍历背包
    //第一行已经赋完值,所以i从1开始
    for(int i=1; i<weight.size(); i++){
        for(int j=0; j<=bagweight; j++){
            if(j<weight[i]){
                dp[i][j]=dp[i-1][j];
            }
            else{
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
            }
        }
    }
    cout<<dp[weight.size()-1][bagweight];

}

int main(){
    fun();
    getchar();
    return 0;
}

一维dp数组0-1背包问题

代码如下:

#include <iostream>
#include <vector>
using namespace std;

void fun(){
    vector<int> weight{1,3,4};
    vector<int> value{15,20,30};
    int bagweight=4;

    //初始化dp数组
    vector<int> dp(bagweight+1,0);

    //遍历dp数组
    //先物品后背包 从后往前倒叙遍历
    for(int i=0; i<weight.size(); i++){
        for(int j=bagweight; j>=weight[i]; j--){
            dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }

    cout<<dp[bagweight]<<endl;
}

int main(){
    fun();
    getchar();
    return 0;
}

1维dp数组,也即滚动数组的0-1背包有几点是需要注意的,首先是与二维不同,一维只能倒叙遍历,因为若从前往后正序遍历,物品会被放入背包两次,就不再是0-1背包问题。再有就是只能先遍历物品再遍历背包,而二维dp数组0-1背包问题遍历方式可以调换。

494.目标和(**)

本题为leetcode中等难度,但我看了题解之后,觉得这种答案我是完全不会想出来,自己还是太菜了,本题要在一组数中每个数前添上加号或者负号,最后相加,使其等于所给的target。按照题解,我们可以将其中添加加号的一组数,命为left;要添加负号的一组数命名为right,则有left+right=sum,left-right=target,其中target和sum是固定的,则可以推出left=(target+sum)/2,则可以转换成dp来解决。二维dp[i][j]的意义则为,从下标0-i的编号任意挑选物品,填满重量为j的背包有多少种组合,其中初始化dp[0][0]=1,其它则为0,二维dp解决此问题代码如下:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0;
        for(int i=0; i<nums.size(); i++){
            sum+=nums[i];
        }
        if(abs(target)>sum) return 0;
        if((sum+target)%2==1) return 0;
        int bagweight=(sum+target)/2;
        vector<vector<int>>dp(nums.size()+1,vector<int>(bagweight+1,0));
        dp[0][0]=1;
        for(int i=1; i<=nums.size(); i++){
            for(int j=0; j<=bagweight; j++){
                if(j<nums[i-1]){
                    dp[i][j]=dp[i-1][j];
                }
                else{
                    dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]];
                }
            }
        }

        return dp[nums.size()][bagweight];

    }
};

本题也可以用一维滚动dp数组解决,dp的递推公式为dp[j]+=dp[j-nums[i]];这个公式后面的完全背包问题也会用到!
在这里插入图片描述

474.一和零

class Solution {
public:
    //有两个容量m和n 正常可用三维dp数组计算
    //但dp[i][][]的值和d[i-1][][]有关 则可以降温到二维dp[][]计算,即
    //滚动数组 二维dp[i][j]的含义是:最多有i个0和j个1的strs的最大子集的大小。。
    // m个0  n个1
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>>dp(m+1,vector<int>(n+1,0));

        for(string str: strs){
            int zeros=0, ones=0;
            for(char c: str){
                if(c=='0'){
                   zeros++; 
                }
                else{
                    ones++;
                }
            }

            for(int i=m; i>=zeros; i--){
                //倒叙
                for(int j=n; j>=ones; j--){
                    dp[i][j]=max(dp[i][j],dp[i-zeros][j-ones]+1);
                }
            }
        }

        return dp[m][n];
    }
};

0-1背包问题总结

在这里插入图片描述
接下来就是完全背包的问题了!!!

518.零钱兑换2(组合问题)

组合问题:先遍历物品,再遍历背包。
代码如下:

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        // int sum=0;
        // for(int i: coins){
        //     sum+=i;
        // }
        // if(amount>sum) return 0;
        //上面注释的写法是错误的,因为相当于完全背包问题物品可以放多次
        //所以就不会有上述的情况
        vector<int> dp(amount+1,0);
        dp[0]=1;
        //先物品再背包
        for(int i=0; i<coins.size(); i++){
            for(int j=coins[i]; j<=amount; j++){
                dp[j]+=dp[j-coins[i]];
            }
        }

        return dp[amount];
    }
};

377.组合总和4(排列问题)

排列问题:先遍历背包,再遍历物品
代码如下:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int>dp(target+1,0);
        dp[0]=1;
        //算排列 先背包 在钱币
        for(int i=1; i<=target; i++){
            for(int j=0; j<nums.size(); j++){
                if(i>=nums[j] && dp[i]+dp[i-nums[j]]<INT_MAX){
                    dp[i]+=dp[i-nums[j]];
                }
            }
        }
        return dp[target];
    }
};

两者区别,一位网友总结的还是非常牛逼:
在这里插入图片描述

70.爬楼梯(进阶版)

在这里插入图片描述

322.零钱兑换

代码如下:

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1,INT_MAX);
        dp[0]=0;

        //完全背包 先遍历物品 在遍历背包 此题与遍历顺序无关
        //正序遍历物品可重复多次放入  0-1背包一维dp倒序遍历
        for(int i=0; i<coins.size(); i++){
            for(int j=0; j<=amount; j++){
                if(j>=coins[i] && dp[j-coins[i]]!=INT_MAX){
                    dp[j]=min(dp[j],dp[j-coins[i]]+1);
                }
            }
        }

        if(dp[amount]==INT_MAX) return -1;
        return dp[amount];
    }
};

本题dp[j]的含义是凑足总额为j所需钱币的最少个数。
在这里插入图片描述

279.完全平方数

代码如下:

// class Solution {
// public:
//     //一维dp
//     int numSquares(int n) {
//         vector<int> dp(n+1,INT_MAX);
//         dp[0]=0;

//         for(int i=1; i*i<=n; i++){
//             for(int j=i*i; j<=n;j++){
//                 dp[j]=min(dp[j],dp[j-i*i]+1);
//             }
//         }
//         return dp[n];
//     }
// };

//二维
class Solution {
public:
    int numSquares(int n) {
        int m = sqrt(n); //#include<cmath>
        vector<vector<int>> dp(m+1, vector<int>(n+1, INT_MAX));
        for (int i = 0; i <= m; i++) {
            dp[i][0] = 0;
        }
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (j >= i*i) {
                    dp[i][j] = min(dp[i-1][j], dp[i][j-i*i]+1);
                }
                else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[m][n];
    }
};

139.单词拆分

本题应注意先遍历背包后遍历物品,即应该注意物品之间的顺序。代码如下:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordset(wordDict.begin(), wordDict.end());
        vector<bool> dp(s.size()+1,false);
        dp[0]=true;
        //先背包 后物品
        for(int i=1; i<=s.size(); i++){
            for(int j=0; j<i; j++){
                string str=s.substr(j,i-j);
                if(wordset.find(str)!=wordset.end() && dp[j]){
                    dp[i]=true;
                }
            }
        }
        return dp[s.size()];
    }
};

在这里插入图片描述

完全背包完结!

多重背包

代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;


//多重背包和0-1背包问题很像,把多重背包数量展开,就是0-1背包问题
void test_multibag(){
    vector<int> weight{1,3,4};
    vector<int> value{15,20,30};
    vector<int> nums{2,3,2};
    int bagweight=10;

    for(int i=0; i<nums.size(); i++){
        while(nums[i]>1){
            weight.push_back(weight[i]);
            value.push_back(value[i]);
            nums[i]--;
        }
    }

    vector<int> dp(bagweight+1,0);

    for(int i=0; i<weight.size(); i++){
        for(int j=bagweight; j>=weight[i]; j--){
            dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
        }
        for(int i=0; i<=bagweight; i++){
            cout<<dp[i]<<" ";
        }
        cout<<endl;
    }



    cout<<dp[bagweight]<<endl;

}

int main(){
    test_multibag();
    getchar();
    return 0;
}

打家劫舍篇!

198.打家劫舍

本题中说只要挨着的两家只有1家被偷,警报就不会响,根据这种逻辑,我们可以列出定义dp数组,并列递推关系。其中dp[i]代表截至到下标为i的位置,可以偷到的最大金额,又可列出递推式子dp[i]=max(dp[i-1],dp[i-2]+nums[i]),即打劫了i-2位置,则不能打劫位置i-1,以及考虑打劫i-1位置,那就不能对当前位置i动手,在两者中取最大值即可。初始化时可知,后续结果均是丛dp[0]和dp[1]而来。
此题中各个商户的店面坐落位置为一条直线,代码如下:

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==1) return nums[0];
        vector<int> dp(nums.size());
        dp[0]=nums[0];
        dp[1]=max(nums[0],nums[1]);
        for(int i=2; i<nums.size(); i++){
            dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
        }
        return dp[nums.size()-1];
    }
};

213.打家劫舍ii

代码如下:

class Solution {
public:
    int rob(vector<int>& nums){
        if(nums.size()==0) return 0;
        if(nums.size()==1) return nums[0];
        int res1=robRange(nums,0,nums.size()-2);//含首不含尾
        int res2=robRange(nums,1,nums.size()-1); //含尾不含首
        return max(res1,res2);
    }

    //198.打家劫舍的逻辑
    int robRange(vector<int>& nums, int start, int end) {
        if(start==end) return nums[start];
        vector<int> dp(nums.size());
        dp[start]=nums[start];
        dp[start+1]=max(nums[start],nums[start+1]);

        for(int i=start+2; i<=end; i++){
            dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
        }

        return dp[end];
    }

};

本文是数组成环的情况,即商户坐落位置成了一个圆形。
在这里插入图片描述

335.打家劫舍iii(树型dp**)

本题,是dp和二叉树结合,卡哥说是树型dp的入门级题目,这个题我认为还是挺有意思的,但我自己能力不够,一开始看这道题目还是没有头绪。说到树肯定就极大概率和递归有关,又需要想到使用哪种遍历方式,是前中后序遍历,还是层序遍历,以后遇到树一定要养成去思考使用哪种遍历的习惯,不要看到此类题目就是一头雾水。此题代码如下:

class Solution {
public:
    int rob(TreeNode* root) {
        vector<int> res=robTree(root);
        return max(res[0], res[1]);
    }

    //后续遍历  vector  0:不偷  1:偷
    vector<int> robTree(TreeNode* cur){
        if(cur==NULL) return {0,0};
        vector<int> left=robTree(cur->left);
        vector<int> right=robTree(cur->right);

        //偷当前父节点, 那么就不能偷左右孩子
        int val1=cur->val+left[0]+right[0];

        //不偷当前父节点,就考虑偷或者不偷左右孩子 取两者之间的最大值
        int val2=max(left[0],left[1])+max(right[0],right[1]);

        return {val2, val1};
    }
};

本题不用去保存所有的res{,},因为在递归的过程中就是在实时地更新到最新的res了。中间的结果也都被保存在栈中。

股票问题篇!

121.买卖股票的最佳时机

整个数组只能买一次,卖一次,代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        if(len==0) return 0;
        vector<vector<int>> dp(len,vector<int>(2));
        //dp[i][0]:第i天持有股票所得最多现金 (就是考虑买入股票花了多少钱)
        //dp[i][1]:第i天不持有股票所得最多现金 (就是考虑卖出股票能赚多少钱)
        dp[0][0]-=prices[0];
        dp[0][1]=0;
        for(int i=1; i<len; i++){
            dp[i][0]=max(dp[i-1][0],-prices[i]); //前一天买,今天买 都是负数 所以取max 看哪个花的少
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]); //前一天卖 和今天卖 看哪个赚的多
        }

        return dp[len-1][1];

    }
};

122.买卖股票的最佳时机2

整个数组,可以买卖多次,但是卖下一支股票时,前一支股票必须卖出。代码如下:

//dp做法:
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        vector<vector<int>> dp(len,vector<int>(2,0));
        dp[0][0]-=prices[0];
        dp[0][1]=0;
        //dp[i][0]:第i天持有股票所得最多现金 (考虑买股票,可能在第i天把之前买得卖了再买,或者在i-1
        //天就买了)
        //dp[i][1]:第i天不持有股票所得最多现金  (考虑卖股票)
        for(int i=1; i<len; i++){
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);//唯一与121不同得地方
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]);
        }
        return dp[len-1][1];
    }
};

123.买卖股票的最佳时机3

整个数组,限制交易两次。代码如下:

class Solution {
public:
    //将交易次数限制为最多两次
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        vector<vector<int>> dp(len,vector<int>(5,0));
        // 1:第一次买入 2:第一次卖出 3:第二次买入 4:第二次卖出
        dp[0][1]=-prices[0];
        dp[0][3]=-prices[0];
        for(int i=1; i<prices.size(); i++){
            dp[i][1]=max(dp[i-1][1], 0-prices[i]);
            dp[i][2]=max(dp[i-1][2], dp[i-1][1]+prices[i]);
            dp[i][3]=max(dp[i-1][3], dp[i-1][2]-prices[i]);
            dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[len-1][4];

    }
};

188.买卖股票的最佳时机4

整个数组限制交易k次,代码如下:

class Solution {
public:
    //奇数是买,偶数是卖   0,1,2,3,4,5,6.....2k
    int maxProfit(int k, vector<int>& prices) {
        int len=prices.size();
        if(len==0) return 0;
        vector<vector<int>> dp(len,vector<int>(2*k+1,0));
        for(int j=1; j< 2*k; j+=2){
            dp[0][j]=-prices[0];
        }

        for(int i=1; i<prices.size(); i++){
            for(int j=1; j<2*k; j+=2){
                //买
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]-prices[i]);
                //卖
                dp[i][j+1]=max(dp[i-1][j+1],dp[i-1][j]+prices[i]);
            }
        }

        return dp[len-1][2*k];
    }
};

309. 最佳买卖股票时机含冷冻期

带冷冻期,即卖出股票后必须休息一天才可以再次购入股票。代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len=prices.size();
        //4个状态:
        //1.持有股票

        //不持有股票:两种卖出股票状态:
        //2.卖出股票:前两天卖出的度过了冷冻期,或者前一天刚卖出股票
        //3.今天刚卖出股票

        //4.今天是冷冻期,但是期限只为一天
        vector<vector<int>> dp(len,vector<int>(4,0));
        dp[0][0]=-prices[0];
        for(int i=1; i<len; i++){
            dp[i][0]=max(dp[i-1][0],max(dp[i-1][1]-prices[i],dp[i-1][3]-prices[i]));
            dp[i][1]=max(dp[i-1][1],dp[i-1][3]);
            dp[i][2]=dp[i-1][0]+prices[i];
            dp[i][3]=dp[i-1][2];
        }
        return max(dp[len-1][3],max(dp[len-1][2],dp[len-1][1]));

    }
};

714.买卖股票的最佳时机含手续费(股票问题完结)

本题与122.买卖股票的最佳时机2唯一不同的地方,就是多了一道手续费的过程,代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int len=prices.size();
        if(len == 0) return 0;
        vector<vector<int>> dp(len, vector<int>(2,0));
        dp[0][0]-=prices[0];
        //dp[i][0] 表示第i天持有股票所省最多现金 (买花的最少)
        //dp[i][1] 表示第i天不持有股票所得最多现金
        for(int i=1; i<len; i++){
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]-fee);//唯一与122.买卖股票的最佳时机2的地方
        }
        return dp[len-1][1];
    }
};

300.最长递增子序列

在这里插入图片描述

代码如下:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size() == 1) return 1;
        int len=nums.size();
        vector<int> dp(len, 1);
        int res=0;
        //dp[i]表示i之前的包括以nums[i]为结尾的最
        //长递增子序列长度
        for(int i=1; i<len; i++){
            for(int j=0; j<i; j++){
                if(nums[i] > nums[j]){
                    //不是单纯的比较dp[i]和dp[j]+1的大小
                    //而是要取最大的dp[j]+1;
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
            res=dp[i]>res?dp[i]:res;
        }
        return res;
    }
};

674.最长连续递增序列

在这里插入图片描述

代码如下:

//暴力
// class Solution {
// public:
//     //[1,3,5,4,7] 输出:3 即[1,3,5]
//     int findLengthOfLCIS(vector<int>& nums) {
//         if(nums.size()==1) return 1;
//         int res=1;
//         vector<int> vc;
//         for(int i=0; i<nums.size()-1; i++){
//             if(nums[i+1]>nums[i]){
//                 res++;
//                 if(i+1 == nums.size()-1){
//                     vc.push_back(res);
//                 }
//             }
//             else{
//                 vc.push_back(res);
//                 res=1;
//             }
//         }
//         int maxvalue=0;
//         for(auto i: vc){
//             if(i>maxvalue){
//                 maxvalue=i;
//             }
//         }
//         return maxvalue;
//     }
// };

//dp
class Solution {
public:
    //[1,3,5,4,7] 输出:3 即[1,3,5]
    int findLengthOfLCIS(vector<int>& nums) {
        int len=nums.size();
        if(len <= 1) return len;

        vector<int> dp(nums.size(),1);
        for(int i=1; i<nums.size(); i++){
            for(int j=i-1; j<i; j++){
                if(nums[i]>nums[j]){
                    dp[i]=dp[j]+1;
                }
                //直接去掉else也可
                // else{
                //     dp[i]=1;
                // }
            }
        }
        return *max_element(dp.begin(),dp.end());
    }
};

718.最长重复子数组

在这里插入图片描述

代码如下:

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        //定义二维dp数组,dp[i][j]:以下标i - 1为结尾的A,
        //和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。
        int len1=nums1.size();
        int len2=nums2.size();
        //注意:定义vector的长度为len+1
        vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));
        dp[0][0]=0;
        int res=-1;
        //注意i<=len1 不只是<
        for(int i=1; i<=len1; i++){
            for(int j=1; j<=len2; j++){
                if(nums1[i-1] == nums2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                res= dp[i][j]>res ? dp[i][j]:res;
            }
        }
        return res;
    }
};

1143.最长公共子序列

在这里插入图片描述

代码如下:

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int len1=text1.size();
        int len2=text2.size();
        vector<vector<int>> dp(len1,vector<int>(len2,0));
        //注意初始化过程,与718.最长重复子数组不同,本题初始化还有else条件
        //因为最长公共子序列不要求连续,并且本题定义的是以下标i结尾,不是i-1结尾
        if(text1[0] == text2[0]){
            dp[0][0]=1;
        }
        for(int i=1; i<len1; i++){
            if(text1[i]==text2[0]){
                dp[i][0]=1;
            }
            else{
                dp[i][0]=dp[i-1][0];
            }
        }

        for(int i=1; i<len2; i++){
            if(text2[i]==text1[0]){
                dp[0][i]=1;
            }
            else{
                dp[0][i]=dp[0][i-1];
            }
        }

        //dp[i][j]:以下标i为结尾的text1,以下标为j结尾的text2,最长
        //公共子序列
        for(int i=1; i<len1; i++){
            for(int j=1; j<len2; 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[len1-1][len2-1];
    }
};

1035.不相交的线

本题与1143.最长公共子序列思路一致,本题定义的dp数组dp[i][j]的含义为到下标i-1为止,和下标j-1为止的最长公共子序列。代码与上题略有不同,体现在初始化的方面,少了一些步骤,代码如下:

class Solution {
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
        int len1=nums1.size();
        int len2=nums2.size();
        vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));

        for(int i=1; i<=len1; i++){
            for(int j=1; j<=len2; 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[len1][len2];
    }
};

53.最大子数组和

在这里插入图片描述
代码如下:

class Solution {
public:
//此题之前做过可用贪心,本次用dp解决
    int maxSubArray(vector<int>& nums) {
        if(nums.size()==1) return nums[0];
        vector<int> dp(nums.size()+1,0);
        int res=-10000;
        for(int i=1; i<=nums.size(); i++){
            dp[i]=max(dp[i-1]+nums[i-1],nums[i-1]);
            if(res<dp[i]){
                res=dp[i];
            }
        }
        return res;
    }
};

编辑距离篇!

392.判断子序列

在这里插入图片描述

class Solution {
public:
//本题可用双指针来做,时间复杂度为O(n) dp时间复杂度O(m*n)
    bool isSubsequence(string s, string t) {
        if(t.size()==0 && s.size()!=0) return false;
        int lens=s.size();
        int lent=t.size();
        vector<vector<int>> dp(lent+1,vector<int>(lens+1,0));
        for(int i=1; i<=lent; i++){
            for(int j=1; j<=lens; j++){
                if(t[i-1] == s[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else{
                    //dp[i][j]=max(dp[i-1][j], dp[i][j-1]);
                    //本题唯一与1143.最长公共子序列不同之处
                    //只需删除t元素,不用将s元素往前退,即
                    dp[i][j]=dp[i-1][j];
            
                }
            }
        }
        if(dp[lent][lens]==s.size()) 
            return true;
        else{
            return false;
        }


    }
};

115.不同的子序列

代码如下:
在这里插入图片描述

class Solution {
public:
    int numDistinct(string s, string t) {
        int lens=s.size();
        int lent=t.size();
        int res=0;
        vector<vector<uint64_t>> dp(lens+1,vector<uint64_t>(lent+1,0));
        //dp[i][j]:以i-1为结尾的s子序列中
        //出现以j-1为结尾的t的个数为dp[i][j]。

        //初始化
        //dp[i][0]=1:由于空字符串是任何字符串的子序列
        //dp[0][j]=0:由于非空字符串不是空字符串的子序列
        //dp[0][0]=1;
        for(int i=0; i<=lens; i++){
            dp[i][0]=1;
        }

        for(int i=1; i<=lens; i++){
            for(int j=1; j<=lent; j++){
                if(s[i-1] == t[j-1]){
                    dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
                }
                else{
                    dp[i][j]=dp[i-1][j];   
                }
            }
        }

        return dp[lens][lent];
    }
};

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

在这里插入图片描述

代码如下:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int len1=word1.size();
        int len2=word2.size();

        vector<vector<int>> dp(len1+1, vector<int>(len2+1,0));
        int res=0;
        for(int i=1; i<=len1; i++){
            for(int j=1; j<=len2; j++){
                if(word1[i-1] == word2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else{
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        //本题代码与1143.最长公共子序列基本一致,只需要在最后
        //分别减去最长公共子序列长度即可
        return len1+len2-2*dp[len1][len2];
    }
};

另一种dp方法如下:

//dp2
class Solution {
public:
    int minDistance(string word1, string word2) {
        int len1=word1.size();
        int len2=word2.size();
        //确定dp数组以及下标的含义
        //dp[i][j]:以i-1为结尾的字符串word1,以j-1为结尾的字符串word2
        //想要达到相等,所需要删除元素的最小次数。

        //2.确定递推关系 3.dp数组初始化
        //当word1[i-1]=word2[j-1]
        //dp[i][j]=dp[i-1][j-1]
        //当word1[i-1] != word2[j-1]
        //dp[i][j]=min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+2)
        //其中dp[i][j-1]+1=dp[i-1][j-1]+2(可能有点绕)
        //因为从字面上理解,dp[i][j-1]本来就不考虑word2[j-1],所以删除
        //word1[i-1],就达到了两个元素都删除的结果,即=dp[i-1][j-1]+2
        vector<vector<int>> dp(len1+1, vector<int>(len2+1,0));

        //初始化
        for(int j=0; j<=len2; j++) dp[0][j]=j;
        for(int i=0; i<=len1; i++) dp[i][0]=i;


        for(int i=1; i<=len1; i++){
            for(int j=1; j<=len2; j++){
                if(word1[i-1] == word2[j-1]){
                    dp[i][j]=dp[i-1][j-1];
                }
                else{
                    dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1);
                }
            }
        }
        return dp[len1][len2];
   
    }
};

72.编辑距离(hard,编辑距离篇完结!)

在这里插入图片描述
代码如下:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int len1=word1.size();
        int len2=word2.size();

        vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));
        //初始化
        for(int i=0; i<=len1; i++){
            dp[i][0]=i;
        }
        for(int j=0; j<=len2; j++){
            dp[0][j]=j;
        }
        //如果word1[i-1] == word2[j-1]
        //则dp[i][j]=dp[i-1][j-1];
        //如果word1[i-1] != word2[j-1]
        //(1)删除word1[i-1],则dp[i][j]=dp[i-1][j]+1
        //(2)删除word2[j-1],则dp[i][j]=dp[i][j-1]+1
        //(3)替换word[i-1],则dp[i][j]=dp[i-1][j-1]+1
        //word2添加一个元素,相当于word1删除一个元素,所以情况分为以上三种
        for(int i=1; i<=len1; i++){
            for(int j=1; j<=len2; j++){
                if(word1[i-1] == word2[j-1]){
                    dp[i][j]=dp[i-1][j-1];
                }
                else{
                    dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i-1][j]+1,dp[i][j-1]+1));
                }
            }
        }

        return dp[len1][len2];
    }
};

647.回文子串

在这里插入图片描述
代码如下:

//dp:
class Solution {
public:
//本题可用dp、双指针、暴力
    int countSubstrings(string s) {
        int len=s.size();
        int res=0;
        vector<vector<bool>> dp(len, vector<bool>(len,false));
        //布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)
        //的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
        for(int i=len-1; i>=0; i--){
        //因为单独一个字符也可以是回文子串,所以从j=i开始
            for(int j=i; j<len; j++){
                if(s[i]==s[j]){
                    if(j-i<=1){
                        res++;
                        dp[i][j]=true;
                    }
                    else if(dp[i+1][j-1]){
                        res++;
                        dp[i][j]=true;
                    }
                }
            }
        }
        return res;
    }
};

//双指针
class Solution {
public:
    int countSubstrings(string s) {
        int res=0;
        int len=s.size();
        for(int i=0; i<len; i++){
            res+=doublepoint(s,i,i,len);
            res+=doublepoint(s,i,i+1,len);
        }
        return res;
    }

    int doublepoint(const string& s, int i, int j, int n){
        int res=0;
        while(i>=0 && j<n && s[i]==s[j]){
            i--;
            j++;
            res++;
        }
        return res;

    }
};

516.最长回文子序列

在这里插入图片描述
代码如下:

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
        //初始化:一个字符的回文子序列长度就是1。
        for (int i = 0; i < s.size(); i++) dp[i][i] = 1;
        
        for (int i = s.size() - 1; i >= 0; i--) {
        //因为是判断子序列,与上题回文子串不同,j从i+1开始
            for (int j = i + 1; j < s.size(); j++) {
                if (s[i] == s[j]) {
                    dp[i][j] = dp[i + 1][j - 1] + 2;
                } else {
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[0][s.size() - 1];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值