5368. 找出数组中的幸运数
首先遍历数组 nums,统计每个数出现的次数,存储在数组 cnt 中,cnt[i] 即为 数字 i 出现的次数。遍历 cnt ,寻找满足 cnt[i] == i 的最大 的 i。
class Solution {
public:
int findLucky(vector<int>& arr) {
int cnt[501] = {0};
for(auto v : arr) {
cnt[v]++;
}
int anw = -1;
for(int i = 1; i <= 500; i++) {
if(cnt[i] == i) {
anw = i;
}
}
return anw;
}
};
5369. 统计作战单位数
首先遍历数组rating,从0开始枚举下标 i ,统计满足 rating[j] < rating[i] 或者 rating[j] > rating[i] ,且 j < i 的 j 的个数。分别存储在lt[i] 和 gt[i] 中。时间复杂度 O(n^2)。 然后遍历 lt 和 gt,从 0 开始枚举下标 i,累加满足 **rating[j] < rating[i] 且 j < i ** 的 lt[j],累加满足 rating[j] > rating[i] 且 j < i 的 gt[j]。
class Solution {
public:
int numTeams(vector<int>& rating) {
int cnt[2][200] = {0};
for(int i = 1; i < rating.size(); i++) {
for(int j = 0; j < i; j++) {
if(rating[j] < rating[i]) {
cnt[0][i]++;
} else if(rating[j] > rating[i]) {
cnt[1][i]++;
}
}
}
int anw = 0;
for(int i = 1; i < rating.size(); i++) {
for(int j = 0; j < i; j++) {
if(rating[j] < rating[i]) {
anw += cnt[0][j];
} else if(rating[j] > rating[i]) {
anw += cnt[1][j];
}
}
}
return anw;
}
};
5370. 设计地铁系统
可以使用 STL 来存储数据,高效实现地铁管理系统🐶。 数据容器的设计可以参见注释。
class UndergroundSystem {
public:
UndergroundSystem() {
}
//所有完成的通行记录
//key.first 为上车站,key.second 为下车站
//value.first 为累计用时,value.second 为完成的通行次数。
map<pair<string, string>, pair<int64_t, int>> data;
//缓存乘客尚未完成的通行记录
//key 为 乘客ID
//value.first 为乘客上车站,value.second 为出发时间。
//乘车下车后,记录应从该容器中移除。
map<int, pair<string, int>> passenger;
void checkIn(int id, string stationName, int t) {
passenger[id] = make_pair(stationName, t);
}
void checkOut(int id, string stationName, int t) {
auto it = passenger.find(id);
data[make_pair(it->second.first, stationName)].first += t - it->second.second;
data[make_pair(it->second.first, stationName)].second ++;
passenger.erase(it);
}
double getAverageTime(string startStation, string endStation) {
auto it = data.find(make_pair(startStation, endStation));
return double(it->second.first)/(it->second.second);
}
};
5371. 找到所有好字符串
可以使用动态规划 + 容斥原理来解决该题。 首先处理特殊数据,如果 s1 > s2 ,直接返回 0 。
然后用容斥原理简化一下问题: 设函数 f(s, evil) 可以计算出字典序不超过 s 且不包含 evil 的字符串的数量。另设置一个变量 cnt,当 s1 包含 evil时, cnt = 0, 否则 cnt = 1。那么最终答案为 f(s2, evil) - f(s1, evil) + cnt。 设置 cnt 的目的是:当 f(s1, evil) 返回的数量中包含 s1 本身时,需要避免 s1 被容斥排除掉。
用动态规划来实现 f(s, evil)。动态规划数组dp[i][j][k]。第一维度表示拼接过程中,当前字符串的长度,第二维度表示拼接过程中,当前字符串是否与 s 的等长前缀相同。 j = 1 表示相同,j = 0 表示不相同。第三维度表示当前字符串的后缀与 evil 的前缀的重叠长度。 当 i = 0时,显然有 dp[0][1][0] = 1,对于其他 dp[0][x][y] 均为 0。 接下来枚举下一个拼接的字符 c ,根据题意计算出从当前状态 dp[i][j][k] 后拼接 c 后转移到 ni,nj,nk。状态转移策略参考代码注释。 f(s, evil)的返回结果记为 dp[n][0][m],dp[n][1][m]的累加和,m∈[0,evil.size()]。
const int64_t MOD = 1000000007;
inline void add(int64_t &a, int64_t b) {
a += b;
if(a >= MOD) {
a %= MOD;
}
}
class Solution {
public:
void get(const string &s, const string &evil, int n, int64_t &sum, int64_t &part) {
int64_t dp[501][2][51] = {0};
int m = evil.size();
dp[0][1][0] = 1;
for(int i = 0; i < n; i++) {
for(int j = 0; j < 2; j++) {
for(int k = 0; k < m; k++) {
for(char c = 'a'; c <= 'z'; c++) {
int ni = i+1, nj = -1, nk = -1;
if(j == 1) {
if(c > s[i]) {
//当前字符串与s前缀相同,
// 所以不能拼接更大的 c。
break;
}
if(c == s[i]) {
//拼接完后仍与s前缀相同,
//所以 nj = 1。
nj = 1;
} else {
//拼接完后小于s前缀,
//所以 nj = 0。
nj = 0;
}
} else {
//已经比s前缀小了,
//无论拼接什么字符,仍然是小。
nj = 0;
}
if(c == evil[k]) {
//已经重叠了k个字符,
//且 c 与 evil的第k+1字符相等,
//所以 nk = k+1
nk = k+1;
} else {
//尝试寻找最大的与evil的重叠长度
nk = 0;
for(int p = 0; p <= k; p++) {
int len = (k-p);
if(evil[len] != c) { continue; }
bool flag = true;
for(int q = 0; q < len; q++) {
if(evil[q] != evil[q+p]) { flag = false; break; }
}
if(flag) { nk = len+1; break;}
}
}
//nk == m 时,说明当前字符串已经包含了 evil
//所以不能记入总数。
if(nk == m) {
continue;
}
add(dp[ni][nj][nk], dp[i][j][k]);
}
}
}
}
sum = 0, part = 0;
for(int i = 0; i < m; i++) {
add(sum, dp[n][0][i]);
add(sum, dp[n][1][i]);
add(part, dp[n][1][i]);
}
}
int findGoodStrings(int n, string s1, string s2, string evil) {
if (s1 > s2) {
return 0;
}
int64_t sum1, part1;
int64_t sum2, part2;
get(s1, evil, n, sum1, part1);
get(s2, evil, n, sum2, part2);
return (sum2+part1-sum1+MOD)%MOD;
}
};