目录
91. 解码方法
难度中等
1406
一条包含字母 A-Z
的消息通过以下映射进行了 编码 :
'A' -> "1" 'B' -> "2" ... 'Z' -> "26"
要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106"
可以映射为:
"AAJF"
,将消息分组为(1 1 10 6)
"KJF"
,将消息分组为(11 10 6)
注意,消息不能分组为 (1 11 06)
,因为 "06"
不能映射为 "F"
,这是由于 "6"
和 "06"
在映射中并不等价。
给你一个只含数字的 非空 字符串 s
,请计算并返回 解码 方法的 总数 。
题目数据保证答案肯定是一个 32 位 的整数。
示例 1:
输入:s = "12" 输出:2 解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
示例 2:
输入:s = "226" 输出:3 解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
示例 3:
输入:s = "06" 输出:0 解释:"06" 无法映射到 "F" ,因为存在前导零("6" 和 "06" 并不等价)
提示:
1 <= s.length <= 100
s
只包含数字,并且可能包含前导零。
class Solution {
int dp[1000];
public:
int numDecodings(string s) {
int n = s.size();
for (int i = 0; i < n; i++)
{
if (i == 0)
{
if (s[i] == '0') break;
else dp[i] = 1;
}
else
{
if(s[i]=='0')
{
if(s[i-1]=='0') break;
if(s[i-1]=='1'||s[i-1]=='2')
{ if(i>1)
dp[i]=dp[i-2];
else
dp[i]=1;
}
else break;
}
else
{
int tep = (s[i - 1] - '0') * 10 + (s[i] - '0');
if(s[i-1]=='0') dp[i]=dp[i-1];
else
{
if(tep>10&&tep<=26)
{
if(i>1) dp[i]=dp[i-2]+dp[i-1];
else dp[i]=2;
}
else
{
if(i>1)
dp[i]=dp[i-1];
else dp[i]=1;
}
}
}
}
}
return dp[n-1];
}
};
1646. 获取生成数组中的最大值
难度简单
83
给你一个整数 n
。按下述规则生成一个长度为 n + 1
的数组 nums
:
nums[0] = 0
nums[1] = 1
- 当
2 <= 2 * i <= n
时,nums[2 * i] = nums[i]
- 当
2 <= 2 * i + 1 <= n
时,nums[2 * i + 1] = nums[i] + nums[i + 1]
返回生成数组 nums
中的 最大 值。
题解:题述相当于状态转移方程
class Solution {
int dp[105];
int maxn=0;
public:
int getMaximumGenerated(int n) {
dp[0]=0;
dp[1]=1;
for(int i=0;i<=n;i++)
{
if(2<=2*i&&2*i<=n) dp[2*i]=dp[i];
if(2<=2*i+1&&2*i+1<=n) dp[2*i+1]=dp[i]+dp[i+1];
maxn=max(dp[i],maxn);
}
return maxn;
}
};
1043. 分隔数组以得到最大和
难度中等
267
给你一个整数数组 arr
,请你将该数组分隔为长度 最多 为 k 的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。
返回将数组分隔变换后能够得到的元素最大和。本题所用到的测试用例会确保答案是一个 32 位整数。
示例 1:
输入:arr = [1,15,7,9,2,5,10], k = 3 输出:84 解释:数组变为 [15,15,15,9,10,10,10]
题解:dp[i] 以下标为i的位置为分割位置,则dp[i]表示i及其前面的最大分割和,只会影响i之前的元素。
class Solution {
int dp[505];
public:
int maxSumAfterPartitioning(vector<int>& arr, int k) {
int n=arr.size();
for(int i=0;i<n;i++)
{
int maxn=0;
dp[i]=0;
for(int j=i;i-j+1<=k&&j>=0;j--)
{
if(arr[j]>maxn)
{
maxn=arr[j];
}
if(j) dp[i]=max(dp[i],dp[j-1]+maxn*(i-j+1));
else dp[i]=max(dp[i],maxn*(i-j+1));
}
}
return dp[n-1];
}
};
139. 单词拆分
难度中等
2185
给你一个字符串 s
和一个字符串列表 wordDict
作为字典。请你判断是否可以利用字典中出现的单词拼接出 s
。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"] 输出: true 解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
class Solution {
int dp[305];
public:
bool wordBreak(string s, vector<string>& wordDict) {
int n=s.size();
int m=wordDict.size();
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
int le=wordDict[j].size();
if(le>i+1) continue;//字典中字符长度大于i,肯定不匹配
int f=0;
int k;
if(i-le!=-1&&dp[i-le]!=1) continue;//即使此字符可以匹配,前面一个紧邻字符未能匹配,也无效。
for( k=i;k>i-le;k--)
{
if(s[k]!=wordDict[j][k-i+le-1]) break;
if(k==i-le+1) dp[i]=1;
}
}
}
if(dp[n-1]) return true;
else return false;
}
};
1277. 统计全为 1 的正方形子矩阵
难度中等
285
给你一个 m * n
的矩阵,矩阵中的元素不是 0
就是 1
,请你统计并返回其中完全由 1
组成的 正方形 子矩阵的个数。
示例 1:
输入:matrix = [ [0,1,1,1], [1,1,1,1], [0,1,1,1] ] 输出:15 解释: 边长为 1 的正方形有 10 个。 边长为 2 的正方形有 4 个。 边长为 3 的正方形有 1 个。 正方形的总数 = 10 + 4 + 1 = 15.
class Solution {
#define maxn 320
bool dp[320][320][320];//开int会超题限内存
public:
int countSquares(vector<vector<int>>& matrix) {
int m=matrix.size();
int n=matrix[0].size();
int le=min(m,n);
int ans=0;
for(int l=1;l<=le;l++)//l为正方形边长
{
for(int i=0;i+l<=m;i++)
{
for(int j=0;j+l<=n;j++)
{
if(l==1 ) dp[l][i][j]=matrix[i][j];
else
{
// if(i>=1&&j>=1)
dp[l][i][j]=matrix[i][j]&dp[l-1][i+1][j]&dp[l-1][i][j+1]&dp[l-1][i+1][j+1];
}
ans+=dp[l][i][j];
}
}
}
return ans;
}
};
剑指 Offer II 091. 粉刷房子
难度中等
154
假如有一排房子,共 n
个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3
的正整数矩阵 costs
来表示的。
例如,costs[0][0]
表示第 0 号房子粉刷成红色的成本花费;costs[1][2]
表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。
示例 1:
输入: costs = [[17,2,17],[16,16,5],[14,3,19]] 输出: 10 解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。 最少花费: 2 + 5 + 3 = 10。
class Solution {
int dp[200][5];
public:
int minCost(vector<vector<int>>& costs) {
int n=costs.size();
memset(dp,0x3f3f3f,sizeof dp);
dp[0][0]=costs[0][0];
dp[0][1]=costs[0][1];
dp[0][2]=costs[0][2];
for(int i=1;i<n;i++)
{
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++)
{
if(j!=k)
dp[i][k]=min(dp[i][k],dp[i-1][j]+costs[i][k]);
}
}
}
int ans=1e9;
for(int i=0;i<3;i++)
{
ans=min(ans,dp[n-1][i]);
}
return ans;
}
};
剑指 Offer 42. 连续子数组的最大和
难度简单
709
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution {
#define maxn 100005
int dp[maxn];
public:
int maxSubArray(vector<int>& nums) {
int n=nums.size();
dp[0]=nums[0];
int ans=nums[0];
for(int i=1;i<n;i++)
{
dp[i]=max(nums[i],dp[i-1]+nums[i]);//dp[i]表示前i个元素中包含num[i]的最大子数组和
ans=max(ans,dp[i]);
}
return ans;
}
};
300. 最长递增子序列
难度中等
3262
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
方案一
class Solution {
int dp[2550];
public:
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
int ans=0;
for(int i=0;i<n;i++)
{
dp[i]=1;
for(int j=0;j<=i;j++)
{
if(nums[i]>nums[j]) dp[i]=max(dp[i],dp[j]+1);
}
ans=max(ans,dp[i]);
}
return ans;
}
};
方案二 贪心,二分
class Solution {
int dp[2550];
public:
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
int ans=1;
dp[0]=-1;
for(int i=1;i<n;i++)
{
if(nums[i]>dp[ans])
{
ans++;
dp[ans]=nums[i];
}
else
{
int l,r,mid;
l=0;r=ans;
while(l<r)//二分查找第一个小于nums[i]的位置,以便将其后一个位置元素变为nums[i]
{
mid=(l+r)>>1;
if(dp[mid]<nums[i]) l++;
else r--;
}
dp[mid+1]=nums[i];
}
}
return ans;
}
};
1027. 最长等差数列
难度中等
320
给你一个整数数组 nums
,返回 nums
中最长等差子序列的长度。
回想一下,nums
的子序列是一个列表 nums[i1], nums[i2], ..., nums[ik]
,且 0 <= i1 < i2 < ... < ik <= nums.length - 1
。并且如果 seq[i+1] - seq[i]
( 0 <= i < seq.length - 1
) 的值都相同,那么序列 seq
是等差的。
示例 1:
输入:nums = [3,6,9,12] 输出:4 解释: 整个数组是公差为 3 的等差数列。
class Solution {
int dp[1005][1005]={0};
public:
int longestArithSeqLength(vector<int>& nums) {
int n=nums.size();
int ans=0;
dp[0][0]=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<i;j++)
{
int k=nums[i]-nums[j]+500;
if(dp[j][k]==0) dp[j][k]=1;//初始化为一
dp[i][k]=dp[j][k]+1;
ans=max(ans,dp[i][k]);
}
}
return ans;
}
};
剑指 Offer II 093. 最长斐波那契数列
难度中等
77
如果序列 X_1, X_2, ..., X_n
满足下列条件,就说它是 斐波那契式 的:
n >= 3
- 对于所有
i + 2 <= n
,都有X_i + X_{i+1} = X_{i+2}
给定一个严格递增的正整数数组形成序列 arr
,找到 arr
中最长的斐波那契式的子序列的长度。如果一个不存在,返回 0 。
(回想一下,子序列是从原序列 arr
中派生出来的,它从 arr
中删掉任意数量的元素(也可以不删),而不改变其余元素的顺序。例如, [3, 5, 8]
是 [3, 4, 5, 6, 7, 8]
的一个子序列)
示例 1:
输入: arr = [1,2,3,4,5,6,7,8] 输出: 5 解释: 最长的斐波那契式子序列为 [1,2,3,5,8] 。
class Solution {
int dp[1005][1005];
unordered_map<int,int> mp;//map哈希加快查找速度
public:
int lenLongestFibSubseq(vector<int>& arr) {
int n=arr.size();
int ans=0;
for(int i=0;i<n;i++)
{ mp[arr[i]]=i+1;
for(int j=i-1;j>=0&&arr[j]*2>arr[i];j--)
{
int tep=arr[i]-arr[j];
int k=mp[tep];
if(k!=0) dp[i][j]=max(dp[j][k-1]+1,3);
ans=max(ans,dp[i][j]);
}
}
return ans;
}
};
剑指 Offer II 095. 最长公共子序列
难度中等
142
给定两个字符串 text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,
"ace"
是"abcde"
的子序列,但"aec"
不是"abcde"
的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace" 输出:3 解释:最长公共子序列是 "ace" ,它的长度为 3 。
class Solution {
int dp[1005][1005];
public:
int longestCommonSubsequence(string text1, string text2) {
text1='@'+text1;
text2='%'+text2;
int le1=text1.size();
int le2=text2.size();
for(int i=1;i<le1;i++)
{
for(int j=1;j<le2;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[le1-1][le2-1];
}
};
516. 最长回文子序列
难度中等
1064
给你一个字符串 s
,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
示例 1:
输入:s = "bbbab" 输出:4 解释:一个可能的最长回文子序列为 "bbbb" 。
题解:转化为求s与s的逆序字符串的最长公共子串(子序列)
class Solution {
int dp[1005][1005];
public:
int longestPalindromeSubseq(string s) {
string s1=s;
reverse(s.begin(),s.end());
s='#'+s;
s1='%'+s1;
for(int i=1;i<s.size();i++)
{
for(int j=1;j<s1.size();j++)
{
if(s[i]==s1[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[s.size()-1][s1.size()-1];
}
};
72. 编辑距离
难度困难
3021
给你两个单词 word1
和 word2
, 请返回将 word1
转换成 word2
所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1:
输入:word1 = "horse", word2 = "ros" 输出:3 解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')
class Solution {
int dp[1005][1005];
public:
int minDistance(string word1, string word2) {
word1='#'+word1;
word2='#'+word2;
int le1=word1.size();
int le2=word2.size();
dp[0][0]=0;
for(int i=1;i<le1;i++)
{
dp[i][0]=dp[i-1][0]+1;
}
for(int i=1;i<le2;i++)
{
dp[0][i]=dp[0][i-1]+1;
}
for(int i=1;i<le1;i++)
{
for(int j=1;j<le2;j++)
{
if(word1[i]==word2[j]) dp[i][j]=dp[i-1][j-1];
else dp[i][j]=min(dp[i-1][j],min(dp[i-1][j-1],dp[i][j-1]))+1;//增,删插三种情况
}
}
return dp[le1-1][le2-1];
}
};
剑指 Offer 47. 礼物的最大价值
难度中等
502
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
示例 1:
输入:[ [1,3,1], [1,5,1], [4,2,1] ]
输出:12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
class Solution {
int dp[205][205];
public:
int maxValue(vector<vector<int>>& grid) {
int m=grid.size();
int n=grid[0].size();
dp[0][0]=grid[0][0];
int i;
for( i=1;i<n;i++)
{
dp[0][i]=dp[0][i-1]+grid[0][i];
}
for( i=1;i<m;i++)
{
dp[i][0]=dp[i-1][0]+grid[i][0];
}
for( i=1;i<m;i++)
{
for(int j=1;j<n;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[m-1][n-1];
}
};
剑指 Offer II 101. 分割等和子集
难度简单
85
给定一个非空的正整数数组 nums
,请判断能否将这些数字分成元素和相等的两部分。
示例 1:
输入:nums = [1,5,11,5] 输出:true 解释:nums 可以分割成 [1, 5, 5] 和 [11] 。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int dp[20005];
int sum=0;
int n=nums.size();
for(int i=0;i<n;i++)
{
sum+=nums[i];
}
if(sum%2) return false;
sum=sum/2;
memset(dp,0,sizeof dp);
dp[0]=1;
for(int i=0;i<n;i++)
{
for(int j=sum;j>=nums[i];j--)
{
if(dp[j-nums[i]]) dp[j]=1;
//或者写成 dp[j]|=dp[j-nums[i]];
}
if(dp[sum])
return true;
}
return false;
}
};
剑指 Offer II 103. 最少的硬币数目
难度中等
84
给定不同面额的硬币 coins
和一个总金额 amount
。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins =[1, 2, 5]
, amount =11
输出:3
解释:11 = 5 + 5 + 1
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int dp[10005];
int n=coins.size();
memset(dp,0x3f3f3f3f,sizeof dp);
dp[0]=0;
for(int i=0;i<n;i++)
{
if(coins[i]<=amount)
dp[coins[i]]=1;
for(int j=coins[i];j<=amount;j++)
{
if(dp[j-coins[i]]) dp[j]=min(dp[j-coins[i]]+1,dp[j]);
}
}
if(dp[amount]<1e5)
return dp[amount];
else
return -1;
}
};