LeetCode(Weekly Contest 182)题解
0. 前言
- 决定重新开始刷 LeetCode,这周参见了周赛
- 中文版地址:https://leetcode-cn.com/contest/weekly-contest-182/
- 英文版地址:https://leetcode.com/contest/weekly-contest-182
1. 题解
1.1 5368. 找出数组中的幸运数(1394. Find Lucky Integer in an Array)
- 中文版题目描述:https://leetcode-cn.com/contest/weekly-contest-182/problems/find-lucky-integer-in-an-array/
- 英文版题目描述:https://leetcode.com/contest/weekly-contest-182/problems/find-lucky-integer-in-an-array/
- 思路:签到题,通过哈希统计各个数的次数,找最大值
- 代码如下:
class Solution { public: int findLucky(vector<int>& arr) { int len = arr.size(); map<int, int> cnt; for (auto v : arr) { cnt[v]++; } int ans = -1; for (auto it = cnt.begin() ; it != cnt.end() ; it++) { if (it->first == it->second) { ans = it->first; } } return ans; } };
1.2 5369. 统计作战单位数(1395. Count Number of Teams)
- 中文版题目描述:https://leetcode-cn.com/classic/problems/count-number-of-teams/description/
- 英文版题目描述:https://leetcode.com/problems/count-number-of-teams/description/
- 思路:暴力求解 O(n^3) 即可实现,但是仔细想想,通过遍历下标为 [1..n-2] 的所有元素,计算每个元素左侧比它大的个数 ll[i]、左侧比它小的个数 ls[i]、右侧比它大的个数 rl[i],右侧比它小的个数 rs[i],最终结果即为 ∑(ll[i]*rs[i]+ls[i]*rl[i])
- 代码如下:
class Solution { public: int numTeams(vector<int>& rating) { int n = rating.size(); int ans = 0; if (n < 3) return 0; for (int i = 1 ; i < n-1 ; i++) { int ll = 0, ls = 0, rl = 0, rs = 0; for (int j = 0 ; j < i ; j++) { if (rating[j] < rating[i]) { ls++; } else if (rating[j] > rating[i]) { ll++; } } for (int j = i+1 ; j < n ; j++) { if (rating[j] < rating[i]) { rs++; } else if (rating[j] > rating[i]) { rl++; } } ans += ls * rl + ll * rs; } return ans; } };
1.3 5370. 设计地铁系统(1396. Design Underground System)
- 中文版题目描述:https://leetcode-cn.com/classic/problems/design-underground-system/description/
- 英文版题目描述:https://leetcode.com/problems/design-underground-system/description/
- 思路:通过两个 Map 记录乘客上车信息和起始站信息,在 checkIn 阶段更新乘客信息,在 checkOut 阶段更新起始站信息,防止每次计算平均时长遍历所有的起始终止路线
- 代码如下:
class UndergroundSystem { public: unordered_map<int, pair<string, int>> passenger; unordered_map<string, unordered_map<string, pair<int, int>>> startEnd; UndergroundSystem() { } void checkIn(int id, string stationName, int t) { passenger[id] = make_pair(stationName, t); } void checkOut(int id, string stationName, int t) { string start = passenger[id].first; if (startEnd[start].find(stationName) == startEnd[start].end()) { startEnd[start][stationName] = make_pair(t-passenger[id].second, 1); } else { startEnd[start][stationName].first += t-passenger[id].second; startEnd[start][stationName].second += 1; } } double getAverageTime(string startStation, string endStation) { return (double)startEnd[startStation][endStation].first / (double)startEnd[startStation][endStation].second; } }; /** * Your UndergroundSystem object will be instantiated and called as such: * UndergroundSystem* obj = new UndergroundSystem(); * obj->checkIn(id,stationName,t); * obj->checkOut(id,stationName,t); * double param_3 = obj->getAverageTime(startStation,endStation); */
1.4 5371. 找到所有好字符串(1397. Find All Good Strings)
- 中文版题目描述:https://leetcode-cn.com/classic/problems/find-all-good-strings/description/
- 英文版题目描述:https://leetcode.com/problems/find-all-good-strings/description/
- 思路:数位 DP + KMP:
- 数位 DP 问题往往都是这样的题型,给定一个闭区间 [l, r],让你求这个区间中满足某种条件的数的总数
- 首先我们将问题转化成更加简单的形式,设 f[i] 表示在区间 [1,i] 中满足条件的数的数量,那么所求的答案就是 f[r] - f[l-1]
- 数位 dp 中,dp 数组永远为 dp[i][state][eq]
- 其中,i 表示前 i 位,即为结果字符串的前缀
- state 描述前缀的状态
- eq 表示前缀是否与 s[0,i) 相等
- dp[i][state][eq] 就是有多少个长度为 i 的前缀,其状态为 state 且相等状态等于 eq
- 在本题中,state 表示前缀与 evil 字符串匹配的最长长度
- 初始化 dp[0][*][1],当长度为 0、匹配长度为0 时,只有 dp[0][0][1] = 1,其它位置都为 0
- 当前匹配 state 长度后,增加一个字符 cur,与 evil 字符串匹配的最大长度怎么计算,根据 kmp 的 next 数组计算即可
- 每次通过 dp[i] 扩展 dp[i+1] 的状态,具体情况可以分为,通过 dp[i][state][0](前缀长期为 i 且小于 s[i:])、dp[i][state][1](前缀长期为 i 且等于 s[i:])扩展出 dp[i+1][ns][0] 和 dp[i+1][ns][1] 等状态
- 其中 ns 为 state 通过 next 数组在添加一个字符 cur 后的最大前缀匹配长度
- 代码如下:
class Solution { public: vector<int> getNextArray(string pattern) { int len = pattern.length(); vector<int> res(len+1,0); int j = 0; for (int i = 1 ; i < len ; i++) { while (j && pattern[i] != pattern[j]) j = res[j]; if (pattern[i] == pattern[j]) j++; res[i+1] = j; } return res; } int getNextState(int state, vector<int>& next, string pattern, char cur) { int j = state; while (j > 0 && pattern[j] != cur) j = next[j]; if (pattern[j] == cur) j++; return j; } bool dec(int n, string& s) { bool result = false; for (int i = n-1 ; i >= 0 ; i--) { if (s[i] > 'a') { s[i]--; result = true; break; } else { s[i] = 'z'; } } return result; } long long dfs(int n, string s, vector<int>& next, string pattern) { long long mod = (long long)1000000007; int lp = pattern.length(); vector<vector<vector<long long>>> dp = vector<vector<vector<long long>>>(n+1, vector<vector<long long>>(lp+1, vector<long long>(2, 0))); dp[0][0][1] = 1; for (int i = 0 ; i < n ; i++) { for (int j = 0 ; j < lp ; j++) { int ns = getNextState(j, next, pattern, s[i]); dp[i+1][ns][1] = (dp[i+1][ns][1] + dp[i][j][1]) % mod; for (char k = 'a' ; k <= 'z' ; k++) { ns = getNextState(j, next, pattern, k); dp[i+1][ns][0] = (dp[i+1][ns][0] + dp[i][j][0]) % mod; } for (char k = 'a' ; k < s[i] ; k++) { ns = getNextState(j, next, pattern, k); dp[i+1][ns][0] = (dp[i+1][ns][0] + dp[i][j][1]) % mod; } } } long long result = (long long)0; for (int i = 0 ; i < lp ; i++) { result = (result + dp[n][i][0]) % mod; result = (result + dp[n][i][1]) % mod; } return result; } int findGoodStrings(int n, string s1, string s2, string evil) { vector<int> next = getNextArray(evil); bool flag = dec(n, s1); long long ans = (long long)0; if (flag) { ans = dfs(n, s2, next, evil) - dfs(n, s1, next, evil); } else { ans = dfs(n, s2, next, evil); } if (ans < 0) { ans += (long long)1000000007; } return (int)ans; } };