// An highlighted block
class Solution {
public:
int fib(int n) {
vector<int> dp(n+1,0);//vector要记得初始化
if(n<1)
return 0;
if(n==1||n==2)
return 1;
for(int i=1;i<=n;i++)
{
if(i==1||i==2)
dp[i]=1;
else
dp[i]=(dp[i-1]+dp[i-2])% 1000000007;//注意取余数
}
return dp[n];
}
};
套路来自:https://labuladong.gitbook.io/algo/
大数求余原因:大数越界
大数越界:随着n增大,f(n)会超过Int32甚至Int64的取值范围,导致最终的返回值错误。
1、 当一个问题只对答案的正确性有要求,而不在乎答案的数值,可能会需要将取值很大的数通过求余变小。2.int32位取值范围是-2147483648~2147483647,1000000007 是最小的十位质数。模1000000007,可以保证值永远在int的范围内。
3.int64位的最大值为2^63-1,对于1000000007来说它的平方不会在int64中溢出 所以在大数相乘的时候,因为(a∗b)%c=((a%c)∗(b%c))%c,所以相乘时两边都对1000000007取模,再保存在int64里面不会溢出
原文链接:https://blog.csdn.net/weixin_46359814/article/details/110109550
class Solution {
public:
int climbStairs(int n) {
//选择 爬1、2台阶
//状态 剩余台阶数
// base n=0 return 0 n=1 return 1
vector<int> dp(n+1,0);
if(n==0)
return 0;
if(n==1)
return 1;
if(n==2)
return 2;
dp[0]=0;
dp[1]=1;
dp[2]=2;
for(int i=3;i<=n;i++)
dp[i]=dp[i-1]+dp[i-2];
return dp[n]; }
};
最值问题
易错点:变量可能有多种状态时,要将每种状态对之后的影响都考虑到
Eg: amount:0(正常结束)
amount: -1(问题不成立)------->res的此次判断无效
res: min(find(amount-coin) in coins)(res作为中间变量正常改变),返回res
res:INT_MAX(初始状态)--------->无有效子问题,返回时应返回-1
vector初始化
1、一维
vector< int > A(n,0);
vector< int > B;
B.resize(n,0);
2、二维
vector > v(2, vector(4,1));
vector > v(2); //2行
// v.resize(2);
for(int i=0;i
https://blog.csdn.net/DreamLike_zzg/article/details/86760751
动态数组
//申请空间
int** a2 = new int*[rows];
for(int i=0;i
https://blog.csdn.net/bqw18744018044/article/details/81665898
代码
class Solution {
public:
vector<int> memo;
int find(vector<int>& coins, int amount) {
if(amount==0)
return 0;//base case
if(amount<0)
return -1;//base case
//状态min(find(amount-coin) in coins)
int res=INT_MAX;
if(memo[amount]!=-3)
return memo[amount];
for(int i =0;i<coins.size();i++)
{
int sub=find(coins,amount-coins[i]);//子问题中的硬币数量;
if(sub==-1)//关键:要排除子问题不成立的情况,此时res=上一次的res------>有可能有res依旧是int_max情况,输出时要记得处理
continue;
res=min(res,sub+1);
}
memo[amount]=res!=INT_MAX?res:-1;//边界条件
return memo[amount];
}
int coinChange(vector<int>& coins, int amount)
{
if(amount<1)
return 0;//边界条件
memo.resize(amount+1,-3);//初始化备忘录
return find(coins,amount);
}
};
这个好理解多了,也没有很多的边界条件判断,推荐。
class Solution {
public:
int coinChange(vector<int>& coins, int amount)
{
vector<int> dp(amount+1,amount+1);
dp[0]=0;
for(int i=1;i<=amount;i++)
{
for(int coin:coins)
{
if(i-coin<0)
continue;
dp[i]=min(dp[i],dp[i-coin]+1);
}
}
return dp[amount]==amount+1?-1:dp[amount];
}
};
状态:字符串 两字符串一起对比下一个
字符不等 s1对比下一个or s2对比下一个
初始条件 0(没有公共子序列)
1、备忘录递归
c++超时了,java可以过
class Solution {
public:
vector<vector<int> > memo;
int longestCommonSubsequence(string text1, string text2) {
memo.resize(text1.length());
for(int i=0;i<text1.length();i++)
{
memo[i].resize(text2.length());
for(int j=0;j<memo[i].size();j++){
memo[i][j]=-1;
}
}
return dp(text1,0,text2,0);
}
int dp(string s1,int i,string s2,int j)
{
if(i==s1.length()||j==s2.length())//结束条件:空串(注意不要指在最后一个字符就结束了,一定到空串)
return 0;
if(memo[i][j]!=-1)
{
return memo[i][j];}
if(s1[i]==s2[j])//base
{memo[i][j]= 1+dp(s1,i+1,s2,j+1);
}
else
memo[i][j]= max({dp(s1,i+1,s2,j),dp(s1,i,s2,j+1)}) ;
return memo[i][j];
}
};
2、自底向上,二维背包
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int**memo=new int*[text1.length()+1];
for(int i=0;i<=text1.length();i++)
{
memo[i]=new int[text2.length()+1];
for(int j=0;j<=text2.length();j++)
memo[i][j]=0;
}
for(int i=1;i<=text1.length();i++)
{
for(int j=1;j<=text2.length();j++)
{
if(text1[i-1]==text2[j-1])
memo[i][j]=1+memo[i-1][j-1];
else
memo[i][j]=max({memo[i-1][j],memo[i][j-1]});
}
}
return memo[text1.length()][text2.length()];}
};
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/edit-distance
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
选择:删除,改变,插入、不变
状态转移:dp[i][j]=min{dp[选择]}
初始化:一方长度为0时,最小编辑次数应为另一方长度。
class Solution {
public:
int minDistance(string word1, string word2) {
int **dp=new int*[word1.length()+1];
for(int i=0;i<=word1.length();i++)
{
dp[i]=new int[word2.length()+1];
for(int j=0;j<=word2.length();j++)
{
dp[i][0]=i;
dp[0][j]=j;
}
}
for(int i=1;i<=word1.length();i++)
{
for(int j=1;j<=word2.length();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,dp[i-1][j-1]+1});
}
}
}
return dp[word1.length()][word2.length()];
}
};
给定两个字符串s1, s2,找到使两个字符串相等所需删除字符的ASCII值的最小和。
1、自底向上(按照题目要求进行)
class Solution {
public:
int minimumDeleteSum(string s1, string s2) {
int**dp=new int*[s1.length()+1];
for(int i=0;i<=s1.length();i++)
{
dp[i]=new int[s2.length()+1];
}
//dp数组的初始化很重要
dp[0][0]=0;
for (int i = 1; i <= s1.length(); ++i) {
dp[i][0] = dp[i - 1][0] + int(s1[i - 1]);
}
for (int j = 1; j <= s2.length(); ++j) {
dp[0][j] = dp[0][j - 1] + int(s2[j - 1]);
}
for(int i=1;i<=s1.length();i++)
{
for(int j=1;j<=s2.length();j++)
{
if(s1[i-1]==s2[j-1])
dp[i][j]=dp[i-1][j-1];
else
dp[i][j]=min({dp[i][j-1]+int(s2[j-1]),dp[i-1][j]+int(s1[i-1])});
}
}
return dp[s1.length()][s2.length()];
}
};
2、最长子序列解法
最小删除ascall码<=>寻找ascall码最大相同子序列
明天写。
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
这是一道一维dp问题,状态转移为dp[n]=max(dp[i] in i
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> dp(nums.size(),1);//初始化,dp(i)定义为num[i]为结尾的最长子序列长度,所以至少是1;
for(int i=0;i<nums.size();i++)
{
for(int j=0;j<i;j++)//注意:由于子序列要求不改变原序列的顺序,所以找小的要从前面找。
{
if(nums[j]<nums[i])
{
dp[i]=max({dp[j]+1,dp[i]});
}
}
}
int res=1;
for(int i=0;i<nums.size();i++)
{
res= max(res,dp[i]);
}
return res;
}
};
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/increasing-triplet-subsequence
如果最长递增子序列>=3则返回true
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
vector<int> dp(nums.size(),1);
for(int i=0;i<nums.size();i++)
{
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])
dp[i]=max(dp[i],dp[j]+1);
}
if(dp[i]>=3)
return true;
}
return false;
}
};
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//dp[n]含义以nums[n]为结尾的子数组的最大和。
//状态转移:dp[n]=max{dp[n-1]+num[n],dp[n]},因为是连续的,所以不需要遍历 i in n 了
//初始化:dp[n]=nums[n];
vector<int > dp(nums.size(),0);
for(int i=0;i<nums.size();i++)
dp[i]=nums[i];
int res=dp[0];
for(int i=1;i<nums.size();i++)
{
dp[i]=max(dp[i-1]+nums[i],dp[i]);
res=max(dp[i],res);
}
return res;
}
};
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
dp[i][j]:以i,j为首尾位置的回文子序列最大长度
状态:子序列首元素与尾元素位置
选择:i+1,j-1 (遍历方向为由下到上,由左到右),因为希望结束在dp[0][length-1]
初始化: i=j时子序列长度为1
class Solution {
public:
int longestPalindromeSubseq(string s) {
int**dp=new int*[s.length()];
for(int i=0;i<s.length();i++)
{dp[i]=new int[s.length()];
for(int j=0;j<s.length();j++)
{ dp[i][j]=0;
if(i==j)
dp[i][j]=1;}
}
for(int i=s.length()-1;i>=0;i--)
{
for(int j=i+1;j<s.length();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.length()-1];
}
};
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
class Solution {
public:
int countSubstrings(string s) {
vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
//dp[i][j]在这两个区间内的子串是否为回文子串
//初始化 i=j时是回文子串 bool=true
//状态转移:如果s[i]!=s[j]则进行下一次遍历,如果相等 dp[i][j]=dp[i+1][j-1]|(j-i<=2); //包含奇数偶数的情况
//状态转移方向:参考最长回文子序列
for(int i=0;i<s.length();i++)
{
for(int j=0;j<s.length();j++)
{
if(i==j)
dp[i][j]=true;
}
}
int count =0;
for(int i=s.length()-1;i>=0;i--)
{
for(int j=i+1; j<s.length();j++)
{
if(s[i]!=s[j])
{
continue;//为什么不是dp[i][j]=dp[i+1][j]|dp[i][j-1]??因为是计算回文子串的数量,是连续的,所以一旦断掉了就进行下一轮遍历了不用状态转移
}
dp[i][j]=dp[i+1][j-1]||(j-i<=2);
}
}
for(int i=0;i<s.length();i++)
{
for(int j=0;j<s.length();j++)
{ if (dp[i][j])
count++;
}
}
return count;
}
};
给你一个字符串 s,找到 s 中最长的回文子串。
方法一:参考回文子串题,将所有回文子串找出来后比大小。O(n^2)
class Solution {
public:
string longestPalindrome(string s) {
vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
//dp[i][j]在这两个区间内的子串是否为回文子串
//初始化 i=j时是回文子串 bool=true
//状态转移:如果s[i]!=s[j]则进行下一次遍历,如果相等 dp[i][j]=dp[i+1][j-1]|(j-i<=2); //包含奇数偶数的情况
//状态转移方向:参考最长回文子序列
for(int i=0;i<s.length();i++)
{
for(int j=0;j<s.length();j++)
{
if(i==j)
dp[i][j]=true;
}
}
int maxlen =1;
for(int i=s.length()-1;i>=0;i--)
{
for(int j=i+1; j<s.length();j++)
{
if(s[i]!=s[j])
{
continue;//为什么不是dp[i][j]=dp[i+1][j]|dp[i][j-1]??因为是计算回文子串的数量,是连续的,所以一旦断掉了就进行下一轮遍历了不用状态转移
}
dp[i][j]=dp[i+1][j-1]||(j-i<=2);
}
}
int begin=0;
for(int i=0;i<s.length();i++)
{
for(int j=0;j<s.length();j++)
{ if (dp[i][j])
if(j-i+1>=maxlen)
{maxlen=j-i+1;
begin=i;}
}
}
return s.substr(begin,maxlen);
}
};
方法二 最坏情况O(n^2),最好情况O(n)
class Solution {
public:
string longestPalindrome(string s) {
string res;
for(int i=0;i<s.length();i++)
{
string s1=find(i,i,s);
string s2=find(i,i+1,s);
res=res.length()>s1.length()?res:s1;
res=res.length()>s2.length()?res:s2;
}
return res;
}
string find(int left,int right,string s)//找到以l,r开始的最长回文子串
{
while(left>=0&&right <s.length()&&s[left]==s[right])//判断数组是否越界
{
left--;
right++;
}
return s.substr(left+1,right-1-left);//substr的使用,数组越界的话会返回空串
}
};
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
class Solution {
public:
vector<vector<string>> res;
vector<string> path;//放已经回文的子串
vector<vector<string>> partition(string s) {
res.clear();
path.clear();
backtrack(s,0);
return res;
}
void backtrack(string &s,int start)
{
if(start>=s.size())
{res.push_back(path);
return;}
for(int i=start;i<s.size();i++)
{
if(isPalindrome(s,start,i))
{string str=s.substr(start,i-start+1);
path.push_back(str);
}
else
continue;
backtrack(s,i+1);
path.pop_back();
}
}
bool isPalindrome(string& s,int start,int end)
{
for(int i=start,j=end;i<j;i++,j--)
{
if(s[i]!=s[j])
return false;
}
return true;
}
};