Leetcode hot100 (day 8,9)
爬楼梯
做法一:小斐波那契数列,只要注意记忆化递归即可
class Solution {
public:
int dp[50];
int climbStairs(int n) {
if(dp[n])return dp[n];
if(n==2)
{
return dp[2]=2;
}
if(n==1)
{
return dp[1]=1;
}
//if(dp[n])return dp[n];
return dp[n]=climbStairs(n-1)+climbStairs(n-2);
}
};
做法二:动态规划,使用滚动数组即可
class Solution {
public:
int climbStairs(int n) {
int l,m,r;
r=1,l=0,m=0;
for(int i=1;i<=n;i++)
{
l=m;
m=r;
r=l+m;
}
return r;
}
};
杨辉三角
相加 注意判断边界
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> ret(numRows);
for(int i=0;i<numRows;i++)
{
ret[i].resize(i+1);
ret[i][0]=ret[i][i]=1;
for(int j=1;j<i;j++)ret[i][j]=ret[i-1][j]+ret[i-1][j-1];
}
return ret;
}
};
打家劫舍
做法:动态规划,dp[n]=max(dp[n-1],dp[n-2]+nums[n])。并且要注意,要先判断nums的大小,不然赋值dp[1]的时候会出现错误。可以用滚动数组
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==1)return nums[0];
vector<int> dp=vector<int>(nums.size(),0);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++)dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
return dp[nums.size()-1];
}
};
完全平方数
做法:动态规划
class Solution {
public:
int numSquares(int n) {
vector<int> f(n+1);
for(int i=1;i<=n;i++)
{
int minn=INT_MAX;
for(int j=1;j*j<=i;j++)
{
minn=min(minn,f[i-j*j]);
}
f[i]=minn+1;
}
return f[n];
}
};
零钱兑换
做法一:记忆化搜索,每个递归都列举所有硬币,向下搜索,amount为0返回0,如果小于0,返回-1,也就是不成功。要注意记忆化,不然会超时
class Solution {
public:
vector<int>count;
int dp(vector<int>&coins,int amount)
{
if(amount<0)return -1;
if(amount==0)return 0;
if(count[amount])
{
return count[amount];
}
int Min=INT_MAX;
for(int coin:coins)
{
int res=dp(coins,amount-coin);
if(res>=0)Min=min(res+1,Min);
}
count[amount]=Min==INT_MAX?-1:Min;
return count[amount];
}
int coinChange(vector<int>& coins, int amount) {
if(amount==0)return 0;
count.resize(amount+1);
return dp(coins,amount);
}
};
做法二:动态规划dp,其实和前面差不多
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int>dp(amount+1,INT_MAX-10);
dp[0]=0;
for(int i=1;i<=amount;i++)
{
for(int coin:coins)
{
if(coin<=i)dp[i]=min(dp[i],dp[i-coin]+1);
}
}
return dp[amount]>=INT_MAX-10?-1:dp[amount];
}
};
单词拆分
做法:动态规划,先用哈希存储word,后续查找为O(1),随后用dp思想即可
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> word;
for(auto dic:wordDict) word.insert(dic);
vector<int>dp(s.size()+1);
dp[0]=1;
for(int i=1;i<=s.size();i++)
{
for(int j=0;j<i;j++)
{
if(dp[j]&&word.find(s.substr(j,i-j))!=word.end())
{
dp[i]=1;
break;
}
}
}
return dp[s.size()];
}
};
最长递增子序列
做法一:动态规划。这里学习到了max_element min_element,返回数组或者vector中最大最小值,加*为值
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> dp(nums.size(),0);
for(int i=0;i<nums.size();i++)
{
dp[i]=1;
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])dp[i]=max(dp[i],dp[j]+1);
}
}
return *max_element(dp.begin(),dp.end());
}
};
做法二:这个有点不好理解,再试试把。优化时间复杂度为O(nlogn)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len=1,n=nums.size();
vector<int> d(n+1,0);
d[len]=nums[0];
for(int i=1;i<n;i++)
{
if(nums[i]>d[len])d[++len]=nums[i];
else
{
int l=1,r=len,pos=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(d[mid]<nums[i])
{
pos=mid;
l=mid+1;
}
else{
r=mid-1;
}
}
d[pos+1]=nums[i];
}
}
return len;
}
};
乘积最大子数组
做法:动态规划。不同的是,要保持一个最大值和最小值,因为如果当前数字为负数,那么说不定前面有负数的情况更大
class Solution {
public:
int maxProduct(vector<int>& nums) {
long maxF=nums[0],minF=nums[0],ans=nums[0];
for(int i=1;i<nums.size();i++)
{
long mx=maxF,mn=minF;
maxF=max(maxF*nums[i],max((long)nums[i],mn*nums[i]));
minF=min(mn*nums[i],min((long)nums[i],mx*nums[i]));
ans=max(maxF,ans);
}
return ans;
}
};
分割等和数组
做法:动态规划 dp[i][j],前i+1个数字,满足和为j为true。这样推上来即可
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n=nums.size();
if(n<2)return false;
int sum=0,maxnum=0;
for(auto &num:nums)
{
sum+=num;
maxnum=max(maxnum,num);
}
if(sum&1)return false;
int target=sum/2;
if(maxnum>target)return false;
vector<int> dp(target+1,0);
dp[0]=true;
for(int i=0;i<n;i++)
{
int num=nums[i];
for(int j=target;j>=num;j--)dp[j]|=dp[j-num];
}
return dp[target];
}
};
最长有效括号
做法一:动态规划,当前为i,如果s[i]==‘(’,那么必然dp[i]=0,如果为')',那么肯定要接着考虑前一位,如果为'(',dp[i]=dp[i-2]+2;如果为')',那么就需要考虑i-dp[i-1]-1是否大于0,以及s[i-dp[i-1]-1]是否为'(',如果是的话还要加上dp[i-dp[i-1]-2]
class Solution {
public:
int longestValidParentheses(string s) {
int maxans=0,n=s.size();
vector<int> dp(n,0);
for(int i=1;i<n;i++)
{
if(s[i]==')')
{
if(s[i-1]=='(')
{
dp[i]=(i>=2?dp[i-2]:0)+2;
}
else if(i-dp[i-1]>0&&s[i-dp[i-1]-1]=='(')
{
dp[i]=dp[i-1]+((i-dp[i-1])>=2?dp[i-dp[i-1]-2]:0)+2;
}
maxans=max(maxans,dp[i]);
}
}
return maxans;
}
};
做法二:栈
class Solution {
public:
int longestValidParentheses(string s) {
int maxans=0;
stack<int> stk;
stk.push(-1);
for(int i=0;i<s.size();i++)
{
if(s[i]=='(')
{
stk.push(i);
}
else
{
stk.pop();
if(stk.empty())
{
stk.push(i);
}
else{
maxans=max(maxans,i-stk.top());
}
}
}
return maxans;
}
};
最长有效路径
做法一:动态规划,f[i][j]=f[i-1][j]+f[i][j-1],同时要注意边界,由于第i行的状态只和第i-1行以及i行有关,所以可以用一维数组来滚动即可
class Solution {
public:
int uniquePaths(int m, int n) {
vector<int> f(n,1);
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)f[j]+=f[j-1];
}
return f[n-1];
}
};
做法二:高中组合数
class Solution {
public:
int uniquePaths(int m, int n) {
long long ans=1;
for(int x=n,y=1;y<m;x++,y++)
{
ans=ans*x/y;
}
return ans;
}
};
最小路径和
做法一:和前面一样,dp即可
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int rows=grid.size(),columns=grid[0].size();
auto dp=vector<vector<int>>(rows,vector<int>(columns));
dp[0][0]=grid[0][0];
for(int i=1;i<rows;i++)
{
dp[i][0]=dp[i-1][0]+grid[i][0];
}
for(int j=1;j<columns;j++)
{
dp[0][j]=dp[0][j-1]+grid[0][j];
}
for(int i=1;i<rows;i++)
{
for(int j=1;j<columns;j++)
{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[rows-1][columns-1];
}
};
最长回文子串
做法一:动态规划,逐步遍历子串长度,从L=2开始
class Solution {
public:
string longestPalindrome(string s) {
int len=s.length();
if(len<2)return s;
int maxlen=1;
int begin=0;
vector<vector<bool>>dp(len,vector<bool>(len));
for(int i=0;i<len;i++)
{
dp[i][i]=true;
}
for(int L=2;L<=len;L++)
{
for(int i=0;i<len;i++)
{
int j=L+i-1;
if(j>=len)break;
if(s[i]!=s[j])dp[i][j]=false;
else
{
if(j-i<3)
{
dp[i][j]=true;
}
else
{
if(j-i<3)
{
dp[i][j]=true;
}
else
{
dp[i][j]=dp[i+1][j-1];
}
}
if(dp[i][j]&&j-i+1>maxlen)
{
maxlen=j-i+1;
begin=i;
}
}
}
}
return s.substr(begin,maxlen);
}
};
做法二:中心扩展,对于每个位置,都看能否扩展
class Solution {
public:
pair<int,int> expand(const string& s,int left,int right)
{
while(left>=0&&right<s.size()&&s[left]==s[right])
{
left--;
right++;
}
return {left+1,right-1};
}
string longestPalindrome(string s) {
int start=0,end=0;
for(int i=0;i<s.size();i++)
{
auto [left1,right1]=expand(s,i,i);
auto [left2,right2]=expand(s,i,i+1);
if(right1-left1>end-start)
{
start=left1;
end=right1;
}
if(right2-left2>end-start)
{
start=left2;
end=right2;
}
}
return s.substr(start,end-start+1);
}
};
最长公共子序列
做法:动态规划
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++)
{
char c1=text1[i-1];
for(int j=1;j<=n;j++)
{
char c2=text2[j-1];
if(c1==c2)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
class Solution {
public:
int minDistance(string word1, string word2) {
int n=word1.size();
int m=word2.size();
if(n*m==0)return n+m;
vector<vector<int>> dp(n+1,vector<int>(m+1));
for(int i=0;i<n+1;i++)dp[i][0]=i;
for(int j=0;j<m+1;j++)dp[0][j]=j;
for(int i=1;i<n+1;i++)
{
for(int j=1;j<m+1;j++)
{
int left=dp[i-1][j]+1;
int down=dp[i][j-1]+1;
int left_down=dp[i-1][j-1];
if(word1[i-1]!=word2[j-1])left_down+=1;
dp[i][j]=min(left,min(down,left_down));
}
}
return dp[n][m];
}
};
燃尽了。希望越来越好