LeetCode 第26场夜喵双周赛 题解

这次题目有点过于简单了…
都是没几行就能干出来的

文章目录

  • a.连续字符
    • a.题目
    • a.分析
    • a.参考代码
  • b.最简分数
    • b.题目
    • b.分析
    • b.参考代码
  • c.统计二叉树中好节点的数目
    • c.题目
    • c.分析
    • c.参考代码
  • d.数位成本和为目标值的最大数字
    • d.题目
    • d.分析
    • d.参考代码

a.连续字符

a.题目

给你一个字符串 s ,字符串的「能量」定义为:只包含一种字符的最长非空子字符串的长度。
请你返回字符串的能量。

示例 1

输入:s = “leetcode”
输出:2
解释:子字符串 “ee” 长度为 2 ,只包含字符 ‘e’ 。

示例 2

输入:s = “abbcccddddeeeeedcba”
输出:5
解释:子字符串 “eeeee” 长度为 5 ,只包含字符 ‘e’ 。

示例 3

输入:s = “triplepillooooow”
输出:5

示例 4

输入:s = “hooraaaaaaaaaaay”
输出:11

示例 5

输入:s = “tourist”
输出:1

提示

  • 1 <= s.length <= 500
  • s 只包含小写英文字母。

a.分析

注意题目的关键 连续一种字符
那么在遍历字符串的时候 会有两种情况:

  • 这个字符和前一个是相同的 所以是连续的 要计数
  • 这个字符和前一个是不同的 所以断开了 把计数重置为1
    然后再在每次计数的时候更新下答案的最大值即可

按照以上思路代码立刻就出来了
总的时间复杂度是遍历字符串的O(n)

a.参考代码

class Solution {
public:
    int maxPower(string s) {
        int ans=1;
        int cnt=1;	//计数
        for(int i=1;i<s.size();i++)
            if(s[i]==s[i-1]){	//连续的
                cnt++;
                ans=max(ans,cnt);	//更新答案
            }
            else cnt=1;	//不连续 重置
        return ans;
    }
};

b.最简分数

b.题目

给你一个整数 n ,请你返回所有 0 到 1 之间(不包括 0 和 1)满足分母小于等于 n最简 分数 。分数可以以 任意 顺序返回。

示例 1

输入:n = 2
输出:[“1/2”]
解释:“1/2” 是唯一一个分母小于等于 2 的最简分数。

示例 2

输入:n = 3
输出:[“1/2”,“1/3”,“2/3”]

示例 3

输入:n = 4
输出:[“1/2”,“1/3”,“1/4”,“2/3”,“3/4”]
解释:“2/4” 不是最简分数,因为它可以化简为 “1/2” 。

示例 4

输入:n = 1
输出:[]

提示

1<= n <= 100

b.分析

看到题目和范围脑海中一现的就是暴力
把符合题目要求的分子分母枚举出来
显然 分母的取值为1~n 而分子的取值为1~分母-1

那么对于每个分数是否是最简 根据高中的知识我们可以知道
当分子和分母没有约数可以化简时候就是最简了
那么我们只需要求一下分子和分母的最大公约数是否为1就行了
由于此处不会出现分子大于分母以及分子分母都为1的情况 所以不需要额外的判断

总的时间复杂度为暴力枚举的O(n^2)以及不值一提的gcd O(logn)
所以一共是O(n^2logn)

b.参考代码

class Solution {
public:
    vector<string> simplifiedFractions(int n) {
        vector<string> ans;
        for(int i=1;i<=n;i++)
            for(int j=1;j<i;j++)
                if(gcd(i,j)==1){	//是最简了
                    string s;
                    s+=to_string(j);
                    s+="/";
                    s+=to_string(i);
                    ans.push_back(s);	//加入答案
                }
        return ans;
    }
    int gcd(int a,int b){return b?gcd(b,a%b):a;}	//gcd辗转相除法 百度学习即可
};

c.统计二叉树中好节点的数目

c.题目

给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。
「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。

示例 1

输入:root = [3,1,4,3,null,1,5]
输出:4
解释:图中蓝色节点为好节点。
根节点 (3) 永远是个好节点。
节点 4 -> (3,4) 是路径中的最大值。
节点 5 -> (3,4,5) 是路径中的最大值。
节点 3 -> (3,1,3) 是路径中的最大值。

示例 2

输入:root = [3,3,null,4,2]
输出:3
解释:节点 2 -> (3, 3, 2) 不是好节点,因为 “3” 比它大。

示例 3

输入:root = [1]
输出:1
解释:根节点是好节点。

提示

  • 二叉树中节点数目范围是 [1, 10^5] 。
  • 每个节点权值的范围是 [-10^4, 10^4] 。

c.分析

观察可以得知 每个节点的最大值只会影响到他及其的子树子树的值对当前节点无影响
因为会影响到它自身 所以树的遍历方式选择先序遍历
先更新完最大值 且判断完当前节点是否加入答案
既然只会影响到子树且子树不影响自己 那么这个当前最大值完全可以作为参数来传递到子树 而不需要额外的数据结构来把子树的影响保存到当前

总的时间复杂度是只有遍历一遍树的节点O(n)

c.参考代码

class Solution {
public:
    int ans=0;
    int goodNodes(TreeNode* root) {
        dfs(root,INT_MIN);	//初始化要最小的保证根节点
        return ans;
    }
    void dfs(TreeNode* t,int Max)
    {
        if(!t)return;	//空的返回
        if(t->val>=Max){	//先更新最大值
            ans++;	并且更新答案
            Max=t->val;
        }
		//传进去是更新完的最大值
        dfs(t->left,Max);
        dfs(t->right,Max);
    }
};

d.数位成本和为目标值的最大数字

d.题目

给你一个整数数组 cost 和一个整数 target 。请你返回满足如下规则可以得到的 最大 整数:

  • 给当前结果添加一个数位(i + 1)的成本为 cost[i] (cost 数组下标从 0 开始)。
  • 总成本必须恰好等于 target
  • 添加的数位中没有数字 0 。

由于答案可能会很大,请你以字符串形式返回。
如果按照上述要求无法得到任何整数,请你返回 “0” 。

示例 1

输入:cost = [4,3,2,5,6,7,2,5,5], target = 9
输出:“7772”
解释:添加数位 ‘7’ 的成本为 2 ,添加数位 ‘2’ 的成本为 3 。所以 “7772” 的代价为 23+ 31 = 9 。 “997” 也是满足要求的数字,但 “7772” 是较大的数字。
数字 成本
1 -> 4
2 -> 3
3 -> 2
4 -> 5
5 -> 6
6 -> 7
7 -> 2
8 -> 5
9 -> 5

示例 2

输入:cost = [7,6,5,5,5,6,8,7,8], target = 12
输出:“85”

解释:添加数位 ‘8’ 的成本是 7 ,添加数位 ‘5’ 的成本是 5 。“85” 的成本为 7 + 5 = 12 。

示例 3

输入:cost = [2,4,6,2,4,6,4,4,4], target = 5
输出:“0”
解释:总成本是 target 的条件下,无法生成任何整数。

示例 4

输入:cost = [6,10,15,40,40,40,40,40,40], target = 47
输出:“32211”

提示

  • cost.length == 9
  • 1 <= cost[i] <= 5000
  • 1 <= target <= 5000

d.分析

非常直观这就是一个完全背包dp问题
在这道题中

  • 背包大小是**target**
  • 总价值得到的字符串数字
  • 重量是**cost[i]**
  • 物品的价值添加 i+1+'0'的数位

另外题目还有额外要求要注意就是 target大小的背包必须全部用完 那么这里参考一般背包的话就要在初始化dp数组留点心

还有一个地方就是 总价值的大小判断
此处我们可以另外开一个函数去处理

  • 字符串长度越大 总价值越大
  • 字符串长度一样 按字典序比较
  • 如果字符串是非法的 那么必定是较小 (必须用完背包空间的做法一般把除了dp[0]外的都赋值为非法值)

由于背包的遍历加入是一个一个的 且是无序加入的 但是字符串显然不同顺序加入会得到不同的总价值
这里需要采用贪心 把cost从后往前加入
因为无序得到物品后 较大的数位肯定全部排列在前总价值会更大
这也恰好符合背包是一个接一个加入的顺序

总的时间复杂度是dp状态的遍历 时间复杂度为O(9target)

d.参考代码

class Solution {
public:
    string largestNumber(vector<int>& cost, int target) {
        vector<string> dp(target+5,"a");	//初始化为非法状态
        dp[0]="";	//只有0空间是合法的
        for(int i=8;i>=0;i--)	//采用一维空间压缩做法
            for(int j=cost[i];j<=target;j++)
            {
                string s=dp[j-cost[i]];
                s.push_back(i+1+'0');
                if(check(s,dp[j]))dp[j]=s;
            }
        return dp[target]=="a"?"0":dp[target];	//注意题目要求 非法即用不完要输出0
    }
    inline bool check(string a,string b)
    {
        if(a[0]=='a')return false;	//非法
        if(b[0]=='a')return true;	//非法
        if(a.size()==b.size())return a>b;	//字典序
        return a.size()>b.size();	//长度
    }
};

你可能感兴趣的:(leetcode,周赛,字符串,数据结构,算法,动态规划,leetcode)