第一题 既不是最小值也不是最大值
题目大意
解析思路
注意到提示中说了数组中每个元素各不相同,所以前三个元素中必有一个满足条件。如果数组长度小于3,则不存在满足条件的数字。
代码实现
class Solution {
public:
int findNonMinOrMax(vector<int>& nums) {
if(nums.size()<3) return -1;
// 对前三个元素排序后,第二个元素满足条件
sort(nums.begin(), nums.begin()+3);
return nums[1];
}
};
第二题 执行子串操作后的字典序最小字符串
题目大意
解题思路
注意几个前提:
- 恰好操作一次:不能不操作
- 子字符串:要求取一个连续的序列
- 字符替换为字母表前一个字符:a变成z会导致字典序变大,其他字符操作后可以让字典序变小
Q1: 如何选择操作的起始位置?
A1: 起始位置越靠前,能让字典序越小。从左往后找第一个不为a的字符的位置作为起始位置。
Q2: 如何选择操作的终止位置?
A2: 从起始位置往右,第一个为a的字符位置的前一个位置。
Q3: 全为a的字符串如何处理?
A3: 全为a的字符串无法通过A3中的方法找到起始位置。将最后一个a变为z可以得到字典序最小的字符串。
代码实现
class Solution {
public:
string smallestString(string s) {
// flag=0表示未找到起始位置,1表示找到了起始位置,2表示找到了终止位置
int flag = 0;
for(int i=0;i<s.size();i++) {
if(flag==0 && s[i]!='a') flag = 1;
else if(flag==1 && s[i]=='a') flag = 2;
if(flag==1) {
s[i] = 'a' + (s[i]-'a'+25)%26;
}
}
// 全为a的序列找不到起始位置,将最后一个a变为z
if(flag == 0) s[s.size()-1] = 'z';
return s;
}
};
第三题 收集巧克力
题目大意
解题思路
原数组为nums,变更后的数组为arr,最小代码数组为cost。
- 初始状态下 arr[i]=nums[i];
- 变更一次后 arr[i]=nums[(i+1+n)%n],此时第i个位置的巧克力有两个选择:被上一轮选中,或者被这一轮选中,取决于哪一轮的arr[i]最小;所以可以推断出cost[i]=min(nums[i], nums[(i+1+n)%n]);
- 变更k次后 arr[i]=nums[(i+k+n)%n], cost[i]=min(cost[i], nums[(i+k+n)%n])。
最后将cost数组求和即为结果。
代码实现
class Solution {
public:
long long minCost(vector<int>& nums, int x) {
int n = nums.size();
long long sum = 0;
for(int i: nums) sum += i;
vector<int> cost = nums;
long long res = sum;
// 依次计算变更k次所需代价,取最小的一个
for(long long k=0;k<n;k++) {
long long ans = k * x;
for(int i=0;i<n;i++) {
cost[i] = min(cost[i], nums[(i+k+n)%n]);
}
for(int i: cost) ans += i;
res = min(res, ans);
}
return res;
}
};
第四题 最大和查询
题目大意
解题思路
由于数组有两个,且每次查询都是二维的,可以联想到二维平面。
(num1[j], nums2[j])、(x[i], y[i])都可以视为二维平面的点,问题转化为求每个(x[i], y[i])右上方的点(num1[j], nums2[j])中最大和的点。
- 考虑到x[i], y[i], nums1[j], nums2[j]的范围在[1, 1e9],而nums1, nums2, queries的长度都在[1, 1e5],可以对x[i], y[i], nums1[j], nums2[j]进行离散化,使其范围落在[1, 1e5]区间。
- 对于y[i]=k多个点(x[i], k),可以用树状数组维护其右边的点(num1[j], nums2[j])的最大和。
- 按y[i]从大到小求出(x[i], y[i])右上的点(num1[j], nums2[j])的最大和。
代码实现
class Solution {
private:
vector<int> tree;
public:
vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
// 离散化
set<int> st;
for(int i: nums1) st.insert(i);
for(int i: nums2) st.insert(i);
for(auto& q: queries) st.insert(q[0]), st.insert(q[1]);
unordered_map<int, int> um;
int size = st.size();
// 此处倒序离散化,有两个原因:
// 1. 坐标大的点排前面
// 2. 树状数组求最大值时比较方便
for(int i: st) um[i]=size--;
size = um.size();
vector<int> sum(nums1.size());
for (int i = 0; i < nums1.size(); i++) {
sum[i] = nums1[i] + nums2[i];
nums1[i] = um[nums1[i]];
nums2[i] = um[nums2[i]];
}
for (int i = 0; i < queries.size(); i++) {
queries[i][0] = um[queries[i][0]];
queries[i][1] = um[queries[i][1]];
}
tree = vector<int>(size+10, -1);
vector<tuple<int, int, int>> vec;
for(int i=0;i<nums1.size();i++) {
vec.push_back({nums1[i], nums2[i], -sum[i]});
}
for(int i=0;i<queries.size();i++) {
vec.push_back({queries[i][0], queries[i][1], i});
}
sort(vec.begin(), vec.end());
vector<int> ans(queries.size());
for (auto [x, y, v] : vec) {
if (v < 0) modify(y, -v);
else ans[v] = query(y);
}
return ans;
}
int query(int idx) {
int res = -1;
for (; idx; idx -= idx&(-idx))
res = max(res, tree[idx]);
return res;
}
void modify(int idx, int val) {
for (; idx < tree.size(); idx += idx&(-idx))
tree[idx] = max(tree[idx], val);
}
};