LeetCode每日一题 2021.11.10-2021.11.20

LeetCode 专栏收录该内容
11 篇文章 0 订阅

2021.11.10 495.提莫攻击

题目

思路

模拟即可,最后一个毒药的持续时间一定是duration秒,因此只需要考察前n-1个毒药是duration秒还是timeSeries[i] - timeSeries[i-1]秒

代码

class Solution {
public:
    int findPoisonedDuration(vector<int>& timeSeries, int duration) {
        int sum = 0;
        int length = timeSeries.size();
        for(int i = 0; i < length; i++) {
            if(i == 0){
                continue;
            } else {
                if(timeSeries[i-1] + duration <= timeSeries[i]) {
                    sum += duration;
                } else{
                    sum += timeSeries[i] - timeSeries[i-1];
                }
            }
        }
        sum += duration;
        return sum;
    }
};

2021.11.11 629.k个逆序对数组

题目

思路

 不优化会超出时间限制

代码

class Solution {
private:
    static constexpr int mod = 1000000007; 
public:
    int kInversePairs(int n, int k) {
        vector<vector<int>> f(2,vector<int>(k+1));
        f[0][0] = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j <= k; j++) {
                int cur = i & 1;//1或者0,初始时为1
                int prev = cur ^ 1;//1或者0,初始时为0
                f[cur][j] = (j - 1 >= 0 ? f[cur][j-1]:0) - (j - i >= 0 ? f[prev][j - i] : 0) + f[prev][j];
                if (f[cur][j] >= mod) {
                    f[cur][j] -= mod;
                } else if (f[cur][j] < 0) {
                    f[cur][j] += mod;
                }

            }
        }
        return f[n & 1][k];
    }
};

2021.11.12 375.猜数字

思路

动态规划题目,让f[i][j]表示猜测i-j最小的开销。

代码

正常写法,区间l逐渐增大,需要注意边界条件,考察f[i][j] k渠道=取到i或者j

class Solution {
public:
    int getMoneyAmount(int n) {
        vector<vector<int>> f(n+1,vector<int>(n+1));
        for(int d = 2; d <= n; d++) {
            for(int i = 1; i <= n-d+1; i++) {
                int j = i + d - 1;
                f[i][j] = INT_MAX;
                for(int x = i + 1; x < j; x ++) {
                    f[i][j] = min(f[i][j],x + max(f[i][x-1],f[x+1][j]));
                }
                f[i][j] = min(f[i][j],i+f[i+1][j]);
                f[i][j] = min(f[i][j],j+f[i][j-1]);
            }
        }
        return f[1][n];
    }
};


 另一种写法,因为既要保证f[i][k-1]和f[k+1][j]都已知,正序遍历不能保证f[k+1][j]已知,所以可以倒着遍历。i从后往前遍历,那么f[k+1][j]一定已知。

class Solution {
public:
    int getMoneyAmount(int n) {
        vector<vector<int>> f(n+1,vector<int>(n+1));
        for (int i = n - 1; i >= 1; i--) {
            for (int j = i + 1; j <= n; j++) {
                int minCost = INT_MAX;
                for (int k = i; k < j; k++) {
                    int cost = k + max(f[i][k - 1], f[k + 1][j]);
                    minCost = min(minCost, cost);
                }
                f[i][j] = minCost;
            }
        }
        return f[1][n];

    }
};

2021.11.13 520.检测大写字母

题目

思路

 无论第一个字母大小写如何,后面的字母必须和第二个字母大小写相同。

第一个字母为小写时,需要判断第二个字母是否为大写。

代码

class Solution {
public:
    bool detectCapitalUse(string word) {
        // 若第 1 个字母为小写,则需额外判断第 2 个字母是否为小写
        if (word.size() >= 2 && islower(word[0]) && isupper(word[1])) {
            return false;
        }
        
        // 无论第 1 个字母是否大写,其他字母必须与第 2 个字母的大小写相同
        for (int i = 2; i < word.size(); ++i) {
            if (islower(word[i]) ^ islower(word[1])) {
                return false;
            }
        }
        return true;
    }
};

2021.11.14 677.键值映射

题目

代码

class MapSum {
public:
    unordered_map<string,int> m;
    MapSum() {

    }
    
    void insert(string key, int val) {
        m[key] = val;
    }
    
    int sum(string prefix) {
        int sum = 0;
        for (unordered_map<string, int>::iterator it = m.begin(); it != m.end(); it++) {
            string s = it->first;
            if(s.find(prefix,0) == 0){
                sum += it->second;
            }
        }
        return sum;
    }
};

/**
 * Your MapSum object will be instantiated and called as such:
 * MapSum* obj = new MapSum();
 * obj->insert(key,val);
 * int param_2 = obj->sum(prefix);
 */

2021.11.15 319.灯泡开关

题目

思路

 第i个灯泡,i开关的次数等约数的个数。

对于 k 而言,如果它有约数 x,那么一定有约数k/x。因此只要当k≠x^2时,约数都是「成对」出现的。这就说明,只有当k是「完全平方数」时,它才会有奇数个约数,否则一定有偶数个约数

因此我们只需要找出 1, 2,⋯,n 中的完全平方数的个数即可,答案即为⌊ \sqrt{n}⌋,⌊⋅⌋ 表示向下取整。

由于\sqrt{n}涉及到浮点数运算,为了保证不出现精度问题,我们可以计算\sqrt{n+\frac{1}{2}} ,这样可以保证计算出来的结果向下取整在 32 位整数范围内一定正确。

代码

class Solution {
public:
    int bulbSwitch(int n) {
        return sqrt(n + 0.5);
    }
};

2021.11.16 391.完美矩形

题目

思路

矩形区域最外围的顶点围起来的面积等于所有小的面积之和同时为了保证不能重叠,除了矩形最外的四个顶点只出现一次,其余顶点均出现2或4次。

代码

typedef pair<int, int> Point;

class Solution {
public:
    bool isRectangleCover(vector<vector<int>>& rectangles) {
        long area = 0;
        int minX = rectangles[0][0], minY = rectangles[0][1], maxX = rectangles[0][2], maxY = rectangles[0][3];
        map<Point, int> cnt;
        for (auto & rect : rectangles) {
            int x = rect[0], y = rect[1], a = rect[2], b = rect[3];
            area += (long) (a - x) * (b - y);

            minX = min(minX, x);
            minY = min(minY, y);
            maxX = max(maxX, a);
            maxY = max(maxY, b);

            Point point1({x, y});
            Point point2({x, b});
            Point point3({a, y});
            Point point4({a, b});

            cnt[point1] += 1;
            cnt[point2] += 1;
            cnt[point3] += 1;
            cnt[point4] += 1;
        }

        Point pointMinMin({minX, minY});
        Point pointMinMax({minX, maxY});
        Point pointMaxMin({maxX, minY});
        Point pointMaxMax({maxX, maxY});
//判断面积是否想等,并且判断最外围的四个顶点是否出现。不判断的话这个样例[0,0,1,1],[0,0,1,1],
//[1,1,2,2],[1,1,2,2]过不了
        if (area != (long long) (maxX - minX) * (maxY - minY) || !cnt.count(pointMinMin) || !cnt.count(pointMinMax) || !cnt.count(pointMaxMin) || !cnt.count(pointMaxMax)) {
            return false;
        }
        //擦除最外围的四个顶点
        cnt.erase(pointMinMin);
        cnt.erase(pointMinMax);
        cnt.erase(pointMaxMin);
        cnt.erase(pointMaxMax);

        for (auto & entry : cnt) {
            int value = entry.second;
            if (value != 2 && value != 4) {
                return false;
            }
        }
        return true;
    }
};

2021.11.16 318.最大单词长度乘积

题目

思路

主要的任务在于判断两个字符串是否有重复的字母。直接遍历判断为O(n)。

可以使用位运算预处理每个单词,通过位运算操作判断两个单词是否有公共字母。由于单词只包含小写字母,共有 26 个小写字母,因此可以使用位掩码的最低 26位分别表示每个字母是否在这个单词中出现。将a 到z 分别记为第 0 个字母到第 25 个字母,则位掩码的从低到高的第 i 位是 1 当且仅当第 i 个字母在这个单词中,0 <= i <= 25。

用数组masks 记录每个单词的位掩码表示。计算数组masks 之后,判断第 i 个单词和第 j 个单词是否有公共字母可以通过判断masks[i] & masks[j] 是否等于 0 实现,当且仅当masks[i] & masks[j]=0 时第 i 个单词和第 j 个单词没有公共字母,此时使用这两个单词的长度乘积更新最大单词长度乘积。

 代码

class Solution {
public:
    int maxProduct(vector<string>& words) {
        int length = words.size();
        vector<int> masks(length);
        for (int i = 0; i < length; i++) {
            string word = words[i];
            int wordLength = word.size();
            for (int j = 0; j < wordLength; j++) {
                masks[i] |= 1 << (word[j] - 'a');
            }
        }
        int maxProd = 0;
        for (int i = 0; i < length; i++) {
            for (int j = i + 1; j < length; j++) {
                if ((masks[i] & masks[j]) == 0) {
                    maxProd = max(maxProd, int(words[i].size() * words[j].size()));
                }
            }
        }
        return maxProd;
    }
};

2021.11.18 563.二叉树的坡度

题目

思路

深度优先遍历,函数dfs返回以root为根节点的所有节点的元素之和,同时更新sum

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int sum = 0;
    int findTilt(TreeNode* root) {
        dfs(root);
        return sum;
    }
    int dfs(TreeNode* root) {//返回以root为根节点的所有节点的元素之和,同时更新sum
        if(root == NULL) {
            return 0;
        }
        int left = dfs(root->left);
        int right = dfs(root->right);
        sum += abs(left - right);
        return left + right + root->val;
    }
};

2021.11.19 397.整数替换

题目

思路

①直接递归,但是得注意越界

class Solution {
public:
    int integerReplacement(int n) {
        if (n == 1) {
            return 0;
        }
        if (n % 2 == 0) {
            return 1 + integerReplacement(n / 2);
        }
        return 2 + min(integerReplacement(n / 2), integerReplacement(n / 2 + 1));
    }
};

②记忆化搜索

class Solution {
private:
    unordered_map<int, int> memo;

public:
    int integerReplacement(int n) {
        if (n == 1) {
            return 0;
        }
        if (memo.count(n)) {
            return memo[n];
        }
        if (n % 2 == 0) {
            return memo[n] = 1 + integerReplacement(n / 2);
        }
        return memo[n] = 2 + min(integerReplacement(n / 2), integerReplacement(n / 2 + 1));
    }
};

③贪心算法

class Solution {
public:
    int integerReplacement(int n) {
        int ans = 0;
        while (n != 1) {
            if (n % 2 == 0) {
                ++ans;
                n /= 2;
            }
            else if (n % 4 == 1) {
                ans += 2;
                n /= 2;
            }
            else {
                if (n == 3) {
                    ans += 2;
                    n = 1;
                }
                else {
                    ans += 2;
                    n = n / 2 + 1;
                }
            }
        }
        return ans;
    }
};

2021.11.20 594.最长和谐子序列

题目

 思路

①先把数组排序,然后找i,j使得|nums[i]-nums[j]|<=1,使得j-i最大。

②使用哈希表记录一下每个元素出现的次数,再遍历每个数组元素x时,查找哈希表中x和x+1的出现的次数,加起来调最大的。

class Solution {
public:
    int findLHS(vector<int>& nums) {
        unordered_map<int, int> cnt;
        int res = 0;
        for (int num : nums) {
            cnt[num]++;
        }
        for (auto [key, val] : cnt) {
            if (cnt.count(key + 1)) {
                res = max(res, val + cnt[key + 1]);
            }
        }
        return res;
    }
};

  • 0
    点赞
  • 3
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 3 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页

打赏作者

dream or nightmare

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值