[力扣刷题总结](每日一题篇)


372. 超级次方

你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出。

示例 1:
输入:a = 2, b = [3]
输出:8

示例 2:
输入:a = 2, b = [1,0]
输出:1024

示例 3:
输入:a = 1, b = [4,3,3,8,5,2]
输出:1

示例 4:
输入:a = 2147483647, b = [2,0,0]
输出:1198

提示:
1 <= a <= 231 - 1
1 <= b.length <= 2000
0 <= b[i] <= 9
b 不含前导 0

解法1:递归+快速幂

思路:
在这里插入图片描述

代码:

class Solution {
public:
    int mod = 1337;
    //递归实现快速幂
    long long myPow(int a, int b){
        if(b == 0) return 1;
        //b为奇数
        else if (b&1) return myPow(a,b-1)*a%mod;
        else{
            long long half = myPow(a,b/2)%mod;
            return half*half%mod;
        }
    }
    int superPow(int a, vector<int>& b) {
        if(b.size() == 0) return 1;
        int n = b.size()-1;
        int curNum = b[n];
        b.pop_back();

        return myPow(a,curNum) * myPow(superPow(a,b),10) %mod;
    }
};

复杂度分析:

时间复杂度:假设 b 数组所代表的数字为 K,使用快速幂的复杂度为 O(logK),或者说是 O(n∗log10),其中 n 为数组 b 的长度,数字 10 所代表的含义是计算一个次方为 10以内的值;而不使用快速幂的复杂度为O(n∗10)

空间复杂度:忽略递归带来的额外空间开销,复杂度为 O(1)

相似题目:50. Pow(x, n)

力扣链接
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn )。

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:

输入:x = 2.10000, n = 3
输出:9.26100
示例 3:

输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25

提示:

-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104

解法1:快速幂+递归

思路:
在这里插入图片描述

代码:

class Solution {
public:
    double fastPow(double x, long long n){
        if(n == 0) return 1;

        if(n % 2 == 1){
            return fastPow(x,n-1)*x;
        }
        double half = myPow(x, n/2);
        return half*half;
    }
    double myPow(double x, int n) {
        long long N = n;
        return N >= 0 ? fastPow(x,N) : 1.0 / fastPow(x,-N);
    }
};

在这里插入图片描述

解法2:快速幂+迭代

代码:

class Solution {
public:
    double fastPow(double x, long long n){
        double res = 1.0;
        double x_ = x;
        while(n > 0){
            if(n % 2 == 1){
                res *= x_;
            }
            x_ *= x_;
            n /= 2;
        }
        return res;
    }
    double myPow(double x, int n) {
        long long N = n;
        return N > 0? fastPow(x,N) : 1.0 / fastPow(x,-N);
    }
};

注意理解
if(n % 2 == 1){res *= x_;}在两种情况下会执行,一种是当N一开始就为奇数,如5时,接下来就会把奇数编程偶数;另一种情况是最后N为1时,由此得到票结果。
在这里插入图片描述

748. 最短补全词

力扣链接
给你一个字符串 licensePlate 和一个字符串数组 words ,请你找出并返回 words 中的 最短补全词 。
补全词 是一个包含 licensePlate 中所有的字母的单词。在所有补全词中,最短的那个就是 最短补全词 。
在匹配 licensePlate 中的字母时:

忽略 licensePlate 中的 数字和空格 。
不区分大小写。
如果某个字母在 licensePlate 中出现不止一次,那么该字母在补全词中的出现次数应当一致或者更多。

例如:licensePlate = “aBc 12c”,那么它的补全词应当包含字母 ‘a’、‘b’ (忽略大写)和两个 ‘c’ 。可能的 补全词 有 “abccdef”、“caaacab” 以及 “cbca” 。

请你找出并返回 words 中的 最短补全词 。题目数据保证一定存在一个最短补全词。当有多个单词都符合最短补全词的匹配条件时取 words 中 最靠前的 那个。

示例 1:
输入:licensePlate = “1s3 PSt”, words = [“step”, “steps”, “stripe”, “stepple”]
输出:“steps”
解释:最短补全词应该包括 “s”、“p”、“s”(忽略大小写) 以及 “t”。
“step” 包含 “t”、“p”,但只包含一个 “s”,所以它不符合条件。
“steps” 包含 “t”、“p” 和两个 “s”。
“stripe” 缺一个 “s”。
“stepple” 缺一个 “s”。
因此,“steps” 是唯一一个包含所有字母的单词,也是本例的答案。

示例 2:
输入:licensePlate = “1s3 456”, words = [“looks”, “pest”, “stew”, “show”]
输出:“pest”
解释:licensePlate 只包含字母 “s” 。所有的单词都包含字母 “s” ,其中 “pest”、“stew”、和 “show” 三者最短。答案是 “pest” ,因为它是三个单词中在 words 里最靠前的那个。

示例 3:
输入:licensePlate = “Ah71752”, words = [“suggest”,“letter”,“of”,“husband”,“easy”,“education”,“drug”,“prevent”,“writer”,“old”]
输出:“husband”

示例 4:
输入:licensePlate = “OgEu755”, words = [“enough”,“these”,“play”,“wide”,“wonder”,“box”,“arrive”,“money”,“tax”,“thus”]
输出:“enough”

示例 5:
输入:licensePlate = “iMSlpe4”, words = [“claim”,“consumer”,“student”,“camera”,“public”,“never”,“wonder”,“simple”,“thought”,“use”]
输出:“simple”

提示:
1 <= licensePlate.length <= 7
licensePlate 由数字、大小写字母或空格 ’ ’ 组成
1 <= words.length <= 1000
1 <= words[i].length <= 15
words[i] 由小写英文字母组成

解法1:统计字符出现次数

代码:

class Solution {
public:
    string shortestCompletingWord(string licensePlate, vector<string>& words) {
        vector<int> cnt(26,0); 
        for(char c:licensePlate){
            if(c>='a' && c <='z'){
                cnt[c-'a']++;
            }
            else if (c>='A' && c <='Z'){
                cnt[c-'A']++;
            }
        }
        string res;
        int idx = -1;

        for(int i = 0;i<words.size();i++){
            vector<int> cntS(26,0);
            for (char c:words[i]){
                cntS[c-'a']++;
            }
            bool ok = true;
            for (int j = 0; j < 26; ++j) {
                if (cntS[j] < cnt[j]) {
                    ok = false;
                    break;
                }
            }
            if (ok && (idx < 0 || words[i].length() < words[idx].length())) {
                idx = i;
            }

        }

        return words[idx];
    }

};

复杂度分析:
在这里插入图片描述

911. 在线选举

力扣链接

给你两个整数数组 persons 和 times 。在选举中,第 i 张票是在时刻为 times[i] 时投给候选人 persons[i] 的。

对于发生在时刻 t 的每个查询,需要找出在 t 时刻在选举中领先的候选人的编号。

在 t 时刻投出的选票也将被计入我们的查询之中。在平局的情况下,最近获得投票的候选人将会获胜。

实现 TopVotedCandidate 类:

TopVotedCandidate(int[] persons, int[] times) 使用 persons 和 times 数组初始化对象。
int q(int t) 根据前面描述的规则,返回在时刻 t 在选举中领先的候选人的编号。

示例:
输入:
[“TopVotedCandidate”, “q”, “q”, “q”, “q”, “q”, “q”]
[[[0, 1, 1, 0, 0, 1, 0], [0, 5, 10, 15, 20, 25, 30]], [3], [12], [25], [15], [24], [8]]
输出:
[null, 0, 1, 1, 0, 0, 1]

解释:
TopVotedCandidate topVotedCandidate = new TopVotedCandidate([0, 1, 1, 0, 0, 1, 0], [0, 5, 10, 15, 20, 25, 30]);
topVotedCandidate.q(3); // 返回 0 ,在时刻 3 ,票数分布为 [0] ,编号为 0 的候选人领先。
topVotedCandidate.q(12); // 返回 1 ,在时刻 12 ,票数分布为 [0,1,1] ,编号为 1 的候选人领先。
topVotedCandidate.q(25); // 返回 1 ,在时刻 25 ,票数分布为 [0,1,1,0,0,1] ,编号为 1 的候选人领先。(在平局的情况下,1 是最近获得投票的候选人)。
topVotedCandidate.q(15); // 返回 0
topVotedCandidate.q(24); // 返回 0
topVotedCandidate.q(8); // 返回 1

提示:
1 <= persons.length <= 5000
times.length == persons.length
0 <= persons[i] < persons.length
0 <= times[i] <= 109
times 是一个严格递增的有序数组
times[0] <= t <= 109
每个测试用例最多调用 104 次 q

解法1:预处理+二分查找

思路:

(1)票选是在离散时间上发生的,每次更新只会对票数+1。 基于此,我们首先要对投票按照时间排序,然后按照时间顺序遍历,找出每个时间点票数最高的人。

(2)计票,我们需要建立一个hashmap,存储每个候选人的票数。
在遍历过程也就是对应到现实中的唱票过程里,我们用votes和top记录票数最高的同学和对应的得票,发先当前得票候选者如果票数更高时,我们就更新top和votes。这样我们就可以得到每时每刻的最高票的候选人

(3)但由于时间是不连续的,最后给出任意一个时间点查询时,我们还需要进行一次二分搜索。
找到到当前时间点最近的一次投票时间点,那个时候对应的最领先的候选人就是答案。

代码:

class TopVotedCandidate {
private:
    vector<int> tops;
    vector<int> times;
public:
    TopVotedCandidate(vector<int>& persons, vector<int>& times) {
        unordered_map<int,int> counts;//候选人->票数
        counts[-1] = -1;
        int top = -1;
        for(auto& p:persons){
            counts[p]++;
            if(counts[p] >= counts[top]) top = p;
            tops.push_back(top);
        }
        this->times = times;
    }
    
    int q(int t) {
        int left = 0, right = times.size()-1;
        while(left<right){
            int mid = left + (right-left+1) / 2;
            if(times[mid]<=t){
                left = mid;
            }else{
                right = mid-1;
            }
        }
        return tops[left];
    }
};

/**
 * Your TopVotedCandidate object will be instantiated and called as such:
 * TopVotedCandidate* obj = new TopVotedCandidate(persons, times);
 * int param_1 = obj->q(t);
 */

复杂度分析:

时间复杂度:预处理的时间复杂度为 O(N),其中 N 为persons 的长度。单次查询的时间复杂度为 O(logN)。

空间复杂度:O(N)。

1610. 可见点的最大数目

力扣链接
给你一个点数组 points 和一个表示角度的整数 angle ,你的位置是 location ,其中 location = [posx, posy] 且 points[i] = [xi, yi] 都表示 X-Y 平面上的整数坐标。

最开始,你面向东方进行观测。你 不能 进行移动改变位置,但可以通过 自转 调整观测角度。换句话说,posx 和 posy 不能改变。你的视野范围的角度用 angle 表示, 这决定了你观测任意方向时可以多宽。设 d 为你逆时针自转旋转的度数,那么你的视野就是角度范围 [d - angle/2, d + angle/2] 所指示的那片区域。

对于每个点,如果由该点、你的位置以及从你的位置直接向东的方向形成的角度 位于你的视野中 ,那么你就可以看到它。

同一个坐标上可以有多个点。你所在的位置也可能存在一些点,但不管你的怎么旋转,总是可以看到这些点。同时,点不会阻碍你看到其他点。

返回你能看到的点的最大数目。

示例 1:

在这里插入图片描述

输入:points = [[2,1],[2,2],[3,3]], angle = 90, location = [1,1]
输出:3
解释:阴影区域代表你的视野。在你的视野中,所有的点都清晰可见,尽管 [2,2] 和 [3,3]在同一条直线上,你仍然可以看到 [3,3] 。

示例 2:
输入:points = [[2,1],[2,2],[3,4],[1,1]], angle = 90, location = [1,1]
输出:4
解释:在你的视野中,所有的点都清晰可见,包括你所在位置的那个点。

示例 3:

在这里插入图片描述

输入:points = [[1,0],[2,1]], angle = 13, location = [1,1]
输出:1
解释:如图所示,你只能看到两点之一。

提示:

1 <= points.length <= 105
points[i].length == 2
location.length == 2
0 <= angle < 360
0 <= posx, posy, xi, yi <= 100

解法1:极角几何+双指针

思路:
在这里插入图片描述

代码:

class Solution {
public:
    int visiblePoints(vector<vector<int>>& points, int angle, vector<int>& location) {
        int x = location[0], y = location[1];
        vector<double> list;//极角
        int cnt = 0;
        double pi = M_PI, t = angle*pi/180;
        for(auto& point:points){
            int a = point[0], b = point[1];
            //所有与原点重合的点都可见,特殊处理
             if (a == x && b == y ) {
                cnt++;
                continue;
             }
            //atan2范围[-π,π],加一个π是为了让角度范围变成[0,2π]
            list.push_back(atan2(b-y,a-x)+pi);
        }
        sort(list.begin(),list.end());
        int n = list.size(), maxVal = 0;
        //将所有点向[2π,4π]拓宽,使得一四象限范围内的点可以被包含
        for(int i = 0;i<n;i++) list.push_back(2*pi+list[i]);
        //滑动窗口+双指针求范围内点最多的区域
        for(int i = 0,j = 0;j<2*n;j++){
            while(i<j && list[j]-list[i]>t) i++;
            maxVal = max(maxVal,j-i+1);
        }
        return maxVal+cnt;
    }
};

1518. 换酒问题

力扣链接
小区便利店正在促销,用 numExchange 个空酒瓶可以兑换一瓶新酒。你购入了 numBottles 瓶酒。

如果喝掉了酒瓶中的酒,那么酒瓶就会变成空的。

请你计算 最多 能喝到多少瓶酒。

示例 1:

在这里插入图片描述

输入:numBottles = 9, numExchange = 3
输出:13
解释:你可以用 3 个空酒瓶兑换 1 瓶酒。
所以最多能喝到 9 + 3 + 1 = 13 瓶酒。

示例 2:

在这里插入图片描述

输入:numBottles = 15, numExchange = 4
输出:19
解释:你可以用 4 个空酒瓶兑换 1 瓶酒。
所以最多能喝到 15 + 3 + 1 = 19 瓶酒。

示例 3:

输入:numBottles = 5, numExchange = 5
输出:6

示例 4:

输入:numBottles = 2, numExchange = 3
输出:2

提示:

1 <= numBottles <= 100
2 <= numExchange <= 100

解法1:模拟

class Solution {
public:
    int numWaterBottles(int numBottles, int numExchange) {
        int count = numBottles;
        while(numBottles >= numExchange){
            int newB = numBottles/numExchange;//新兑换的酒
            int restB = numBottles%numExchange;//剩余的未兑换的空瓶子
            count += newB;
            numBottles = newB + restB;//当前的空瓶子
        }
        return count;
    }
};

686. 重复叠加字符串匹配

力扣链接
给定两个字符串 a 和 b,寻找重复叠加字符串 a 的最小次数,使得字符串 b 成为叠加后的字符串 a 的子串,如果不存在则返回 -1。

注意:字符串 “abc” 重复叠加 0 次是 “”,重复叠加 1 次是 “abc”,重复叠加 2 次是 “abcabc”。

示例 1:

输入:a = “abcd”, b = “cdabcdab”
输出:3
解释:a 重复叠加三遍后为 “abcdabcdabcd”, 此时 b 是其子串。

示例 2:

输入:a = “a”, b = “aa”
输出:2

示例 3:

输入:a = “a”, b = “a”
输出:1

示例 4:

输入:a = “abc”, b = “wxyz”
输出:-1

提示:

1 <= a.length <= 104
1 <= b.length <= 104
a 和 b 由小写英文字母组成

解法1:字符串匹配

思路:
(1)STL 中 string 容器有一个 find 函数,是解决子串问题的神器。如果确实不想学 KMP 算法的话,可以自行去了解一下这个函数。

string 中 find() 返回值是子串在母串中的位置(下标记录),如果没有找到,那么会返回一个特别的标记 npos 。(返回值可以看成是一个 int 型的数)

(2)思路分析: 这道题看起来很简单,但是如果没有找到切入点还是比较难以下手的。
首先我们应该知道,如果A的长度大于等于B的长度,这时B是A的重复叠加字符串只有两种情况,
第一种就是本身B就是A的子串(比如A = “abcdefg”, B = “bcd”),
第二种就是B是两个A的子串(A = “abcdefg”, B = “efgab”, 2 * A == “abcdefgabcdefg”)。

如果A的长度小于B的长度,这时B是A的重复子串,则A的重复次数不超过 Bsize / Asize + 2
其中“Bsize / Asize”代表的B串中间A重复的次数,“+2”代表的首尾各添加一个A串。

代码:

class Solution {
public:
    int repeatedStringMatch(string a, string b) {
        int aLen = a.size();
        int bLen = b.size();
        if(aLen >= bLen){
            string aDouble = a + a;
            if(a.find(b)!=a.npos){
                return 1;
            }else if(aDouble.find(b) != aDouble.npos){
                return 2;
            }else return -1;
        }

        int count = 1;
        string aTemp = a;
        while(count <= bLen/aLen + 2){
            if(aTemp.find(b) != aTemp.npos){
                return count;
            }
            aTemp += a;
            count++;
        }
        return -1;
    }
};

简化:

class Solution {
public:
    int repeatedStringMatch(string a, string b) {
        int aLen = a.size();
        int bLen = b.size();
        
        int count = 1;
        string aTemp = a;
        while(count <= bLen/aLen + 2){
            if(aTemp.find(b) != aTemp.npos){
                return count;
            }
            aTemp += a;
            count++;
        }
        return -1;
    }
};

507. 完美数

力扣链接
对于一个 正整数,如果它和除了它自身以外的所有 正因子 之和相等,我们称它为 「完美数」。

给定一个 整数 n, 如果是完美数,返回 true,否则返回 false

示例 1:

输入:num = 28
输出:true
解释:28 = 1 + 2 + 4 + 7 + 14
1, 2, 4, 7, 和 14 是 28 的所有正因子。

示例 2:

输入:num = 6
输出:true

示例 3:

输入:num = 496
输出:true

示例 4:

输入:num = 8128
输出:true

示例 5:

输入:num = 2
输出:false

提示:

1 <= num <= 108

解法1:枚举

思路:
在这里插入图片描述

代码:

class Solution {
public:
    bool checkPerfectNumber(int num) {
        if(num == 1) return false;
        int numA = 1;
        for(int i = 2;i*i<=num;i++){
            if(num % i == 0){
                numA += i;
                if(i*i != num) numA += num/i;
            } 
        }
        return numA == num;
    }
};

在这里插入图片描述

390. 消除游戏

力扣链接
列表 arr 由在范围 [1, n] 中的所有整数组成,并按严格递增排序。请你对 arr 应用下述算法:

从左到右,删除第一个数字,然后每隔一个数字删除一个,直到到达列表末尾。
重复上面的步骤,但这次是从右到左。也就是,删除最右侧的数字,然后剩下的数字每隔一个删除一个。
不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
给你整数 n ,返回 arr 最后剩下的数字。

示例 1:

输入:n = 9
输出:6
解释:
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr = [2, 4, 6, 8]
arr = [2, 6]
arr = [6]
示例 2:

输入:n = 1
输出:1

提示:

1 <= n <= 109

解法1:等差数列模拟

思路:
在这里插入图片描述
在这里插入图片描述

代码:

class Solution {
public:
    int lastRemaining(int n) {
        int a1 = 1, an = n;
        int k = 0, cnt = n, step = 1;

        while(cnt > 1){
            if(k % 2 == 0){//正向
                a1 = a1 + step;
                an = cnt % 2 == 0 ? an : an - step;
            }else{//反向
                a1 = cnt % 2 == 0 ? a1 : a1 + step;
                an = an - step;
            }
            k++;
            //个数减半
            cnt = cnt >> 1;
            //公差整半
            step = step << 1;
        }
        return a1;
    }
};

复杂度分析:

时间复杂度:O(logn),其中 n 为初始整数列表的元素数目。每次删除都会将元素数目减半,所以时间复杂度为 O(logn)。

空间复杂度:O(1)。只需要使用常数的额外空间。

1576. 替换所有的问号

力扣链接
给你一个仅包含小写英文字母和 ‘?’ 字符的字符串 s,请你将所有的 ‘?’ 转换为若干小写字母,使最终的字符串不包含任何 连续重复 的字符。

注意:你 不能 修改非 ‘?’ 字符。

题目测试用例保证 除 ‘?’ 字符 之外,不存在连续重复的字符。

在完成所有转换(可能无需转换)后返回最终的字符串。如果有多个解决方案,请返回其中任何一个。可以证明,在给定的约束条件下,答案总是存在的。

示例 1:

输入:s = “?zs”
输出:“azs”
解释:该示例共有 25 种解决方案,从 “azs” 到 “yzs” 都是符合题目要求的。只有 “z” 是无效的修改,因为字符串 “zzs” 中有连续重复的两个 ‘z’ 。
示例 2:

输入:s = “ubv?w”
输出:“ubvaw”
解释:该示例共有 24 种解决方案,只有替换成 “v” 和 “w” 不符合题目要求。因为 “ubvvw” 和 “ubvww” 都包含连续重复的字符。
示例 3:

输入:s = “j?qg??b”
输出:“jaqgacb”
示例 4:

输入:s = “??yw?ipkj?”
输出:“acywaipkja”

提示:

1 <= s.length <= 100

s 仅包含小写英文字母和 ‘?’ 字符

解法1:字符串+模拟

思路:
在这里插入图片描述

代码:

class Solution {
public:
    string modifyString(string s) {
        if(s[0] == '?') s[0] = 'a';
        for(int i = 1;i<s.size();i++) {
            if(s[i] == '?') s[i] = (s[i-1] - 'a' + 1) % 26 + 'a';
            else if(s[i] == s[i-1]) {
                s[i-1] = (s[i-1]-'a'+1)%26 +'a';
            }
        }
        return s;
    }
};

1629. 按键持续时间最长的键

力扣链接
LeetCode 设计了一款新式键盘,正在测试其可用性。测试人员将会点击一系列键(总计 n 个),每次一个。

给你一个长度为 n 的字符串 keysPressed ,其中 keysPressed[i] 表示测试序列中第 i 个被按下的键。releaseTimes 是一个升序排列的列表,其中 releaseTimes[i] 表示松开第 i 个键的时间。字符串和数组的 下标都从 0 开始 。第 0 个键在时间为 0 时被按下,接下来每个键都 恰好 在前一个键松开时被按下。

测试人员想要找出按键 持续时间最长 的键。第 i 次按键的持续时间为 releaseTimes[i] - releaseTimes[i - 1] ,第 0 次按键的持续时间为 releaseTimes[0] 。

注意,测试期间,同一个键可以在不同时刻被多次按下,而每次的持续时间都可能不同。

请返回按键 持续时间最长 的键,如果有多个这样的键,则返回 按字母顺序排列最大 的那个键。

示例 1:

输入:releaseTimes = [9,29,49,50], keysPressed = “cbcd”
输出:“c”
解释:按键顺序和持续时间如下:
按下 ‘c’ ,持续时间 9(时间 0 按下,时间 9 松开)
按下 ‘b’ ,持续时间 29 - 9 = 20(松开上一个键的时间 9 按下,时间 29 松开)
按下 ‘c’ ,持续时间 49 - 29 = 20(松开上一个键的时间 29 按下,时间 49 松开)
按下 ‘d’ ,持续时间 50 - 49 = 1(松开上一个键的时间 49 按下,时间 50 松开)
按键持续时间最长的键是 ‘b’ 和 ‘c’(第二次按下时),持续时间都是 20
‘c’ 按字母顺序排列比 ‘b’ 大,所以答案是 ‘c’

示例 2:

输入:releaseTimes = [12,23,36,46,62], keysPressed = “spuda”
输出:“a”
解释:按键顺序和持续时间如下:
按下 ‘s’ ,持续时间 12
按下 ‘p’ ,持续时间 23 - 12 = 11
按下 ‘u’ ,持续时间 36 - 23 = 13
按下 ‘d’ ,持续时间 46 - 36 = 10
按下 ‘a’ ,持续时间 62 - 46 = 16
按键持续时间最长的键是 ‘a’ ,持续时间 16

提示:

releaseTimes.length == n
keysPressed.length == n
2 <= n <= 1000
1 <= releaseTimes[i] <= 109
releaseTimes[i] < releaseTimes[i+1]
keysPressed 仅由小写英文字母组成

解法1:字符串

代码:

class Solution {
public:
    char slowestKey(vector<int>& releaseTimes, string keysPressed) {
        char result = 'a';
        int maxcount = 0;
        
        for(int i = 0;i<keysPressed.size();i++){
            int count = i == 0? releaseTimes[i] : releaseTimes[i] - releaseTimes[i-1];
            char c = keysPressed[i];
            if(count > maxcount){
                maxcount = count;
                result = c;
            }else if(count == maxcount && c > result){
                result = c;
            }
        }

        return result;
    }
};

2034. 股票价格波动

力扣链接
给你一支股票价格的数据流。数据流中每一条记录包含一个 时间戳 和该时间点股票对应的 价格 。

不巧的是,由于股票市场内在的波动性,股票价格记录可能不是按时间顺序到来的。某些情况下,有的记录可能是错的。如果两个有相同时间戳的记录出现在数据流中,前一条记录视为错误记录,后出现的记录 更正 前一条错误的记录。

请你设计一个算法,实现:

更新 股票在某一时间戳的股票价格,如果有之前同一时间戳的价格,这一操作将 更正 之前的错误价格。
找到当前记录里 最新股票价格 。最新股票价格 定义为时间戳最晚的股票价格。
找到当前记录里股票的 最高价格 。
找到当前记录里股票的 最低价格 。
请你实现 StockPrice 类:

StockPrice() 初始化对象,当前无股票价格记录。
void update(int timestamp, int price) 在时间点 timestamp 更新股票价格为 price 。
int current() 返回股票 最新价格 。
int maximum() 返回股票 最高价格 。
int minimum() 返回股票 最低价格 。

示例 1:

输入:
[“StockPrice”, “update”, “update”, “current”, “maximum”, “update”, “maximum”, “update”, “minimum”]
[[], [1, 10], [2, 5], [], [], [1, 3], [], [4, 2], []]
输出:
[null, null, null, 5, 10, null, 5, null, 2]

解释:
StockPrice stockPrice = new StockPrice();
stockPrice.update(1, 10); // 时间戳为 [1] ,对应的股票价格为 [10] 。
stockPrice.update(2, 5); // 时间戳为 [1,2] ,对应的股票价格为 [10,5] 。
stockPrice.current(); // 返回 5 ,最新时间戳为 2 ,对应价格为 5 。
stockPrice.maximum(); // 返回 10 ,最高价格的时间戳为 1 ,价格为 10 。
stockPrice.update(1, 3); // 之前时间戳为 1 的价格错误,价格更新为 3 。
// 时间戳为 [1,2] ,对应股票价格为 [3,5] 。
stockPrice.maximum(); // 返回 5 ,更正后最高价格为 5 。
stockPrice.update(4, 2); // 时间戳为 [1,2,4] ,对应价格为 [3,5,2] 。
stockPrice.minimum(); // 返回 2 ,最低价格时间戳为 4 ,价格为 2 。

提示:

1 <= timestamp, price <= 109
update,current,maximum 和 minimum 总 调用次数不超过 105 。
current,maximum 和 minimum 被调用时,update 操作 至少 已经被调用过 一次 。

解法1:哈希表 + 有序集合

代码:

class StockPrice {
private:
    int t_max = 0;
    unordered_map<int,int> umap;// 时间-价格 哈希表
    multiset<int> mset;// 可重复有序集合
public:
    StockPrice() {

    }
    
    void update(int timestamp, int price) {
        t_max = max(timestamp,t_max);// 更新最新时间
        auto it = umap.find(timestamp);
        if(it!=umap.end()){// 哈希表中存在该时间点
            mset.erase(mset.find(it->second));// 有序集合中移除该价格
            it->second = price;// 更新哈希表中该时间点的价格
        }else{// 哈希表中不存在该时间点,直接在哈希表上记录该时间点的价格
            umap[timestamp] = price;
        }
        mset.insert(price);// 有序集合加入该价格
    }
    
    int current() {
        return umap[t_max];// 返回最新时间点的价格
    }
    
    int maximum() {
        return *mset.rbegin();// 返回有序集合尾元素
    }
    
    int minimum() {
        return * mset.begin();// 返回有序集合头元素
    }
};

/**
 * Your StockPrice object will be instantiated and called as such:
 * StockPrice* obj = new StockPrice();
 * obj->update(timestamp,price);
 * int param_2 = obj->current();
 * int param_3 = obj->maximum();
 * int param_4 = obj->minimum();
 */

在这里插入图片描述

884. 两句话中的不常见单词

力扣链接
句子 是一串由空格分隔的单词。每个 单词 仅由小写字母组成。

如果某个单词在其中一个句子中恰好出现一次,在另一个句子中却 没有出现 ,那么这个单词就是 不常见的 。

给你两个 句子 s1 和 s2 ,返回所有 不常用单词 的列表。返回列表中单词可以按 任意顺序 组织。

示例 1:

输入:s1 = “this apple is sweet”, s2 = “this apple is sour”
输出:[“sweet”,“sour”]

示例 2:

输入:s1 = “apple apple”, s2 = “banana”
输出:[“banana”]

提示:

1 <= s1.length, s2.length <= 200
s1 和 s2 由小写英文字母和空格组成
s1 和 s2 都不含前导或尾随空格
s1 和 s2 中的所有单词间均由单个空格分隔

解法1:字符串+哈希表

思路:

可以理解成拼接字符串A+B,然后返回拼接后的字符串中只出现过一次的单词

代码:

class Solution {
public:
    vector<string> uncommonFromSentences(string s1, string s2) {
        string s = s1 + " " + s2;
        unordered_map<string,int> umap;
        int start = 0;
        for(int i = 0;i<s.size();i++){
            if(s[i] == ' ') {
                umap[s.substr(start,i-start)]++;
                start = i+1;
            }
            else if(i == s.size() - 1) umap[s.substr(start,i-start+1)]++;
        }
        vector<string> res;
        for(auto& s:umap){
            if(s.second == 1) res.push_back(s.first);
        }
        return res;
    }
};

1763. 最长的美好子字符串

力扣链接
当一个字符串 s 包含的每一种字母的大写和小写形式 同时 出现在 s 中,就称这个字符串 s 是 美好 字符串。比方说,“abABB” 是美好字符串,因为 ‘A’ 和 ‘a’ 同时出现了,且 ‘B’ 和 ‘b’ 也同时出现了。然而,“abA” 不是美好字符串因为 ‘b’ 出现了,而 ‘B’ 没有出现。

给你一个字符串 s ,请你返回 s 最长的 美好子字符串 。如果有多个答案,请你返回 最早 出现的一个。如果不存在美好子字符串,请你返回一个空字符串。

示例 1:

输入:s = “YazaAay”
输出:“aAa”
解释:“aAa” 是一个美好字符串,因为这个子串中仅含一种字母,其小写形式 ‘a’ 和大写形式 ‘A’ 也同时出现了。
“aAa” 是最长的美好子字符串。
示例 2:

输入:s = “Bb”
输出:“Bb”
解释:“Bb” 是美好字符串,因为 ‘B’ 和 ‘b’ 都出现了。整个字符串也是原字符串的子字符串。
示例 3:

输入:s = “c”
输出:""
解释:没有美好子字符串。
示例 4:

输入:s = “dDzeE”
输出:“dD”
解释:“dD” 和 “eE” 都是最长美好子字符串。
由于有多个美好子字符串,返回 “dD” ,因为它出现得最早。

提示:

1 <= s.length <= 100
s 只包含大写和小写英文字母。

解法1:字符串+位运算

思路:
对于某个子串而言,我们只关心大小写是否同时出现,而不关心出现次数。

因此我们无须使用二维数组来记录具体的词频,可以在枚举子串时,使用两个 int 的低 2626 位分别记录大小写字母的出现情况,利用枚举子串时右端点后移,维护两变量,当且仅当两变量相等时,满足 2626 个字母的大小写同时出现或同时不出现。

在这里插入图片描述

代码:

class Solution {
public:
    string longestNiceSubstring(string s) {
        int n = s.size();
        string result = "";
        int maxLen = 0;
        for(int i = 0;i<n;i++){
            int lower = 0, upper = 0;
            for(int j = i;j<n;j++){
                if(s[j]>='A'&& s[j]<='Z') upper |= 1<< (s[j] - 'A');
                else if(s[j]>='a'&& s[j]<='z') lower |= 1<<(s[j] - 'a');
                if(lower == upper && j-i+1 > maxLen) {
                    maxLen = j-i+1;
                    result = s.substr(i,j-i+1);
                }
            }
        }
        return result;
    }
};

在这里插入图片描述

解法2 :字符串+哈希表

思路:
使用s构造集合set1,使用s的全大写版构造集合set2,

如果set1的长度恰好是set2的两倍,则易证s是美好字符串。

代码:

class Solution {
public:
    string longestNiceSubstring(string s) {
        int n = s.size();
        string result = "";
        int maxLen = 0;
        for(int i = 0;i<n;i++){
            unordered_set<char> uset1;
            unordered_set<char> uset2;
            for(int j = i;j<n;j++){
                uset1.insert(s[j]);
                if(s[j]>='a'&& s[j]<='z') uset2.insert(s[j]-'a'+'A');
                else uset2.insert(s[j]);
                if(uset1.size() == 2*uset2.size() && j-i+1 > maxLen) {
                    maxLen = j-i+1;
                    result = s.substr(i,j-i+1);
                }
                }
            }
        return result;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姬霓钛美

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值