你的任务是计算 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
代码:
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)
力扣链接
实现 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
代码:
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);
}
};
代码:
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时,由此得到票结果。
力扣链接
给你一个字符串 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] 由小写英文字母组成
代码:
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];
}
};
力扣链接
给你两个整数数组 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。 基于此,我们首先要对投票按照时间排序,然后按照时间顺序遍历,找出每个时间点票数最高的人。
(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)。
力扣链接
给你一个点数组 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
代码:
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;
}
};
力扣链接
小区便利店正在促销,用 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
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;
}
};
力扣链接
给定两个字符串 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)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;
}
};
力扣链接
对于一个 正整数,如果它和除了它自身以外的所有 正因子 之和相等,我们称它为 「完美数」。
给定一个 整数 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
代码:
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;
}
};
力扣链接
列表 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
代码:
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)。只需要使用常数的额外空间。
力扣链接
给你一个仅包含小写英文字母和 ‘?’ 字符的字符串 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 仅包含小写英文字母和 ‘?’ 字符
代码:
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;
}
};
力扣链接
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 仅由小写英文字母组成
代码:
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;
}
};
力扣链接
给你一支股票价格的数据流。数据流中每一条记录包含一个 时间戳 和该时间点股票对应的 价格 。
不巧的是,由于股票市场内在的波动性,股票价格记录可能不是按时间顺序到来的。某些情况下,有的记录可能是错的。如果两个有相同时间戳的记录出现在数据流中,前一条记录视为错误记录,后出现的记录 更正 前一条错误的记录。
请你设计一个算法,实现:
更新 股票在某一时间戳的股票价格,如果有之前同一时间戳的价格,这一操作将 更正 之前的错误价格。
找到当前记录里 最新股票价格 。最新股票价格 定义为时间戳最晚的股票价格。
找到当前记录里股票的 最高价格 。
找到当前记录里股票的 最低价格 。
请你实现 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 操作 至少 已经被调用过 一次 。
代码:
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();
*/
力扣链接
句子 是一串由空格分隔的单词。每个 单词 仅由小写字母组成。
如果某个单词在其中一个句子中恰好出现一次,在另一个句子中却 没有出现 ,那么这个单词就是 不常见的 。
给你两个 句子 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 中的所有单词间均由单个空格分隔
思路:
可以理解成拼接字符串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;
}
};
力扣链接
当一个字符串 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 只包含大写和小写英文字母。
思路:
对于某个子串而言,我们只关心大小写是否同时出现,而不关心出现次数。
因此我们无须使用二维数组来记录具体的词频,可以在枚举子串时,使用两个 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;
}
};
思路:
使用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;
}
};