开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
力扣——第91场双周赛
2465. 不同的平均值数目 - 力扣(LeetCode)
问题解析
将数组排序后,用头尾双指针模拟数被删除,用哈希表记录下这两个数的平均值,最后返回哈希表大小即可。
AC代码
class Solution {
public:
int distinctAverages(vector<int>& nums) {
sort(nums.begin(),nums.end());
int l=0,r=nums.size()-1;
unordered_map<double,int>mymap;
while(l<r)
{
mymap[((double)nums[l]+nums[r])/2.0]++;
l++,r--;
}
return mymap.size();
}
};
2466. 统计构造好字符串的方案数 - 力扣(LeetCode)
问题解析
一开始想组合数求解,后来发现动态规划更简单。
设置二维状态转移数组f,f[i] [0]表示长度为i且以0结尾的方案数个数,f[i] [1]表示长度为i且以1结尾的方案数个数。初始f[0] [0]为1。
- 当i>=zero时,有状态转移方程:f[i] [0]=(f[i-zero] [0] + f[i-zero] [1]);
- 当i>=one时,有状态转移方程:f[i] [1]=(f[i-one] [0] + f[i-one] [1]);
因为题目说长度在low和high之间的字符串都满足条件,所以我们记录长度在low和high之间的方案数。
AC代码
class Solution {
public:
long long f[200050][2],MOD=1e9+7;
int countGoodStrings(int low, int high, int zero, int one) {
f[0][0]=1;
long long sum=0;
for(int i=1;i<=high;i++)
{
if(i>=zero)f[i][0]=(f[i-zero][0]+f[i-zero][1])%MOD;
if(i>=one)f[i][1]=(f[i-one][0]+f[i-one][1])%MOD;
if(i>=low&&i<=high)
{
sum=(sum+f[i][0]+f[i][1])%MOD;
}
}
return sum;
}
};
2467. 树上最大得分和路径 - 力扣(LeetCode)
问题解析
有意思的题。
首先我们应该知道,Alice从根节点0往下走可以有很多条不同的路,但是Bob从节点往根节点0走就只有一条路。那么我们可以先让Bob走完他走的路,用数组Bob记录下每个节点的状态:Bob[i]表示Bob走到节点i用了几步,为-1说明BOb没走到这个节点过。
然后Alice从根节点进行dfs,同时记录Alice走的步数,走到一个新的点x时,看一下Bob[x]的状态:
- 如果Bob[x]为-1或Bob[x]大于Alice的步数,Alice可以获得这个点的全部分数,因为Bob走不到这个点,或者说Bob晚于Alice到达这个点;
- 如果Bob[x]小于Alice的步数,Alice什么也得不到,因为这个门被Bob第一个打开了;
- 如果Bob[x]等于Alice的步数,Alice和Bob共享这扇门的分数。
当到达叶子时,计算一下这条路径的总分数,并维护最大值。
最后返回最大值即可。
AC代码
class Solution {
public:
vector<int>tree[100050];
int deep[100050],Bob[100050],Alice[100050],mx=-1e9;
vector<int>nums;
//第一个dfs,记录每个节点的深度,方便判断Bob走路的方向
void dfs(int x,int y,int u)
{
deep[x]=u;
for(auto&i:tree[x])
{
if(i==y)continue;
dfs(i,x,u+1);
}
}
//第二个dfs,记录Bob的路径,因为是往上走,所以每次要走到深度比当前点小的点
void dfs2(int x,int u)
{
Bob[x]=u;
for(auto&i:tree[x])
{
if(deep[i]<deep[x])
dfs2(i,u+1);
}
}
//第三个dfs,Alice开始行动
void dfs3(int x,int y,int num,int u)
{
bool flag=true;
if(Bob[x]==u)
num+=nums[x]/2;
else if(Bob[x]!=-1&&Bob[x]<u)
num+=0;
else
num+=nums[x];
for(auto&i:tree[x])
{
if(i==y)continue;
dfs3(i,x,num,u+1);
flag=false;
}
if(flag)
mx=max(mx,num);
}
int mostProfitablePath(vector<vector<int>>& edges, int bob, vector<int>& amount) {
nums=amount;
for(auto&i:edges)
{
tree[i[0]].push_back(i[1]);
tree[i[1]].push_back(i[0]);
}
memset(Bob,-1,sizeof Bob);
dfs(0,-1,1);
dfs2(bob,0);
dfs3(0,-1,0,0);
return mx;
}
};
2468. 根据限制分割消息 - 力扣(LeetCode)
问题解析
很奇怪的题目。
主要就是咱们不知道字符串会分成多少部分,多少部分会影响每一部分的字符串长度(除去结尾的<a/b>)。
但是这个字符串的计算方式我们是知道的,就是每一部分都加上<a/b>罢了,我们很容易就算出多出来的这一部分的长度,即:a的长度+b的长度+3(“</>"的长度),我们可以一个个枚举b的大小(即枚举分成多少份能满足条件。
算出所有b部分的"<a/b>"的长度加上字符串本身的长度,如果小于等于b*limit并且大于等于(b-1) *limit,说明能满足题目要求(大于等于(b-1) *limit是因为除了最后一部分,其余的部分都要有limit个字符串),那么我们就按照b部分来分割它即可。
AC代码
class Solution {
public:
int get_len(int x)
{
int len = 0;
while (x)
{
x /= 10;
len++;
}
return len;
}
vector<string> check(string s, int limit, int cnt)
{
vector<string>v;
int n = s.size(), b = get_len(cnt), len = b + 4, a = 1;
string str;
for (int i = 0; i < n; i++)
{
str += s[i];
if (str.size() + len == limit)
{
str += '<' + to_string(a) + '/' + to_string(cnt) + '>';
a++;
len = b + get_len(a) + 3;
v.push_back(str);
str.clear();
}
}
if (str.size() != 0)
{
str += '<' + to_string(a) + '/' + to_string(cnt) + '>';
v.push_back(str);
}
return v;
}
vector<string> splitMessage(string message, int limit) {
int n = message.size(), len = 0;
for (int i = 1; i <= n; i++)
{
if (i == 7)
{
cout << endl;
}
if (i == 10)
len += 9;
else if (i == 100)
len += 99;
else if (i == 1000)
len += 999;
else if (i == 10000)
len += 9999;
len += get_len(i) * 2 + 3;
int x = len + n;
if (i > 1 && x <= i * limit && x > (i - 1) * limit)
{
return check(message, limit, i);
}
else if (i == 1 && x <= limit)
{
return check(message, limit, i);
}
}
return {};
}
};