2020-12-20 第 220 场周赛
5629. 重新格式化电话号码
思路:非常简单的模拟题,先把数字给取出来,然后三个三个拼接,最后只剩四维的时候在分别讨论即可。
class Solution {
public String reformatNumber(String number) {
StringBuffer res = new StringBuffer();
StringBuffer s = new StringBuffer();
for(int i = 0;i < number.length();i++) {
if(Character.isDigit(number.charAt(i))) {
s.append(number.charAt(i));
}
}
int n = s.length();
for(int i = 0;i < n;i += 3) {
if(n - i <= 4) {
if(n-i == 2) {
res.append(s.substring(i,i+2));
} else if(n-i == 3) {
res.append(s.substring(i,i+3));
} else if(n-i == 4) {
res.append(s.substring(i,i+2));
res.append("-");
res.append(s.substring(i+2,i+4));
}
} else {
res.append(s.substring(i,i+3));
res.append("-");
}
}
return res.toString();
}
}
5630. 删除子数组的最大得分
思路:第一感觉就是双指针,模拟删除数组的所有可能。简单来说就是先遍历每一个下标,该下标表示删除的子数组以当前下标为结尾,那么因为每个元素的都是大于0的,所以希望左边界越小越好。
那么怎么确定左边界呢?如果一个数组是不重复的,比如:[1,2,3,4]
,那么删除所有的元素即可。如果有重复,如[4,5,1,3,4]
,那么左边界就可以取到当前数字上次出现的位置。还有一种情况,[4,5,1,2,5,4]
,如果当前遍历到了最后一个元素,那么上次出现4
的位置中间依旧存在重复元素。所以在遍历右边界过程中,左边界只能一直增大或不变,而不能减小。遍历完有边界之后,取过程中出现的最大值即可。
class Solution {
public int maximumUniqueSubarray(int[] nums) {
int n = nums.length;
int[] sum = new int[n];
//前缀和数组,用来来取一段连续区间的值
for(int i = 0;i < n;i++) {
sum[i] = nums[i];
if(i > 0) sum[i] += sum[i-1];
}
//Map记录每个数字上次出现的位置
Map<Integer, Integer> map = new HashMap<>();
int start = -1,res = 0;
for(int i = 0;i < n;i++) {
int lastPos = map.getOrDefault(nums[i] , -1);
// 左边界只能增大或者不变
start = Math.max(start, lastPos);
int cur = sum[i] - (start >= 0 ? sum[start] : 0);
res = Math.max(res, cur);
map.put(nums[i],i);
}
return res;
}
}
5631. 跳跃游戏 VI
思路:动态规划 + 优先队列。考虑每一个下标
i
,表示跳到当前位置的最大得分,而我们只能从与i
距离不超过k
的位置跳过来。如果考虑遍历i
前面的k
个位置的最大得分,就会超时。所以这时候,选择用优先队列,存储两个值,一个是某个位置上的得分,另一个是该得分所在的位置。
遍历到每一个i
位置时,考虑优先队列的队首元素,如果队首元素的位置与当前位置已经超过k
,那么之后也一定超过k
,所以不再考虑,直接弹出。重复以上操作,直至队列为空,或队首元素的位置与当前位置小于k
,那么该队首元素位置的得分一定是能够跳到i
位置的最大得分(因为优先队列)。
class Solution {
public int maxResult(int[] nums, int k) {
PriorityQueue<Integer[]> q = new PriorityQueue<>(new Comparator<Integer[]>() {
@Override
public int compare(Integer[] o1, Integer[] o2) {
if(o1[0] != o2[0]) {
return o2[0] - o1[0];
}
return o2[1] - o1[1];
}
});
int n = nums.length,res = 0;
for(int i = 0;i < n;i++) {
int Mx = 0;
while(q.size() > 0) {
Integer[] c = q.peek();
int v = c[0], idx = c[1];
// 如果和当前位置距离超过K,则弹出;否则队首元素的得分就是最大值。
if(i-idx > k) {
q.poll();
continue;
} else {
Mx = v;
break;
}
}
q.offer(new Integer[]{Mx + nums[i],i});
if(i == n-1) res = Mx + nums[i];
}
return res;
}
}
5632. 检查边长度限制的路径是否存在
思路 (该思路出自讨论区,我也是从这里学习到解题思路的):先分别对询问和边权进行排序,然后依次回答询问,每次回答询问前,将所有小于询问边权的边加入图中,然后判定联通性。
由于这里询问的边权进行了排序,所以只需要不断往图中加边,而不需要删边,判断连通性用并查集即可。
const int maxn = 1e5 + 10;
int pre[maxn];
//====================并查集模板=============================
//查找上级
int find_pre(int x)
{
int r = pre[x];
while(pre[r] != r)
{
r = pre[r];
}
//路径压缩
while(x != r)
{
int tmp = pre[x];
pre[x] = r;
x = tmp;
}
return r;
}
//合并
void join(int x,int y)
{
int fx = find_pre(x);
int fy = find_pre(y);
if(fx == fy) return;
if (fx > fy) swap(fx, fy);
pre[fx] = fy;
}
void init(int n)
{
for(int i = 0;i <= n;i++)
{
pre[i] = i;
}
}
//====================并查集模板=============================
struct Node {
int u,v;
int limit;
int idx;
} query[maxn];
bool cmp(Node a, Node b) {
return a.limit < b.limit;
}
class Solution {
public:
vector<bool> distanceLimitedPathsExist(int N, vector<vector<int>>& e, vector<vector<int>>& queries) {
int n = e.size(), m = queries.size();
vector<bool> res(m);
init(N);
for(int i = 0;i < m;i++) {
query[i].u = queries[i][0];
query[i].v = queries[i][1];
query[i].limit = queries[i][2];
query[i].idx = i;
}
sort(e.begin(), e.end(), [&](auto& e1, auto& e2) {
return e1[2] < e2[2];
});
sort(query, query + m, cmp);
for(int i = 0,j = 0;i < m;i++) {
//查询前,先把小于限制边权的边加入图中
while(j < n && e[j][2] < query[i].limit) {
join(e[j][0],e[j][1]);
j++;
}
// 判断连通性
int fu = find_pre(query[i].u);
int fv = find_pre(query[i].v);
if(fu == fv) {
res[query[i].idx] = true;
} else {
res[query[i].idx] = false;
}
}
return res;
}
};