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