我又回来了,上周周赛也做了,但是因为比较忙,所以没有更新博客。这周周赛虽然简单,但仍然只做出了两题,第三题一直卡在倒数第二个测试用例,因为竞赛是不给看测试用例的具体内容的,所以一致没找到自己的错误在哪里。。
第一题–5838. 检查字符串是否为数组前缀
思路
这一题还是非常简单的,使用words中的元素顺序拼接,当拼接字符串的长度等于s的长度时,比较两个字符串是否相等,相等则返回true,否则返回false;因为可能拼接失败——拼接字符串的长度直接大于s的长度,此时直接返回false即可。
代码
class Solution {
public boolean isPrefixString(String s, String[] words) {
int len = words.length;
String cur = "";
for (int i= 0;i<len;i++)
{
cur += words[i];
if (cur.length() == s.length())
{
return cur.equals(s);
}
if(cur.length() > s.length())
return false;
}
return false;
}
}
第二题–5839. 移除石子使总数最小
思路
因为数据的范围为10的5次方,所以最多可以使用O(n* logn)时间复杂度的算法。
因为需要求的是石子的总数最小,所以我们每次找到总数最大的石子堆,对其进行减少即可达到要求。
这里使用优先队列来保证每次取的都是当前数量最多的石子堆;
代码
class Solution {
public int minStoneSum(int[] piles, int k) {
int len = piles.length;
int ans = 0;
//使用优先队列,让数量最大的石子堆始终在堆顶
PriorityQueue<Integer> p = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer t1) {
return t1-i1;
}
});
for (int i : piles)
{
p.add(i);
}
//重复k次,每次都把堆顶的石子堆取出来,按照要求进行减少,然后再放回优先队列内,因为优先队列的性质,会对其中的元素自动进行排序,所以放入后,优先队列的堆顶还是数量最大的石子堆
for (int i=0;i<k;i++)
{
int cur = p.poll();
cur = cur - (int) Math.floor(cur/2);
p.add(cur);
}
//优先队列内的元素一次出队,计算石子总数
while (!p.isEmpty())
ans += p.poll();
return ans;
}
}
第三题–5840. 使字符串平衡的最小交换次数
思路
因为数据范围的要求,所以时间复杂度只能等于或低于O(n)才不会超时。
原本我的思路是:
- 使用队列消除当前字符串中所有满足条件的“[”和"]";
- 然后把队列内剩余的元素存为新的字符串,交换第一个和最后一个元素,交换次数加一,再次使用队列消除满足条件的“[”和"]";
- 重复第二步操作,直至当前字符串的长度为0。
但是在57/58测试用例超时了。。。
比赛结束后,看了其他大佬的代码后发现,根本不需要模拟替换过程。。。。,而我代码超时的原因是因为:
在使用队列消除满足条件的“[”和"]“后,此时队列的内容一定是形如”]]]]…[[[[“的,所以,每次将第一个和最后一个字符进行交换,一次可以消除四个字符。
我的方法因为重复循环的原因,在数据非常大,且原本就是”]]]]…[[[["格式时,就会超时。
因此得到如下的代码
代码
class Solution {
public int minSwaps(String s) {
//使用队列消除满足条件的“[”和"]"
Deque<Character> d = new LinkedList<>();
for (int i=0;i<s.length();i++)
{
if (s.charAt(i)==']' && !d.isEmpty() && d.peekLast()=='[')
d.pollLast();
else
d.addLast(s.charAt(i));
}
//获取当前队列的长度
int size = d.size();
//因为一次交换会消除四个字符,所以size/4;当size不是4的整数倍时,需要额外的一次交换,所以加一
return size%4==0?size/4:size/4+1;
}
}
第四题–5841. 找出到每个位置为止最长的有效障碍赛跑路线
思路
emm这个题在竞赛时因为第三题卡住了,所以根本没看。。
比赛结束后回来看一下
emmm,其实这个题挺简单的,早知道做不成来第三题先做这个了。
简单来说,问题可以转换为
对于数组obstacles中的第i个元素,在前i-1个元素中有多少个不大于obstacles[i]的元素个数,再加一,即为最终答案ans[i]对应的值。
因为数据范围为
1
0
5
10^5
105,所以时间复杂度最高为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),正好可以使用二分查找来依次寻找每一个元素对应的答案。
得到如下代码
代码
class Solution {
public int[] longestObstacleCourseAtEachPosition(int[] obstacles) {
int len = obstacles.length;
//设置结果数组
int ans[] = new int[len];
//使用List存储当前元素之前的出现过的元素,按照从小到大的顺序排列,方便后续的二分查找
List<Integer> list = new ArrayList<>();
for (int i=0;i<len;i++)
{
int cur = obstacles[i];
//因为list始终维持其升序的状态,所以可以使用二分查找寻找第一个大于cur的索引位置
int l=0, r=list.size();
while (l<r)
{
int mid = l + (r-l)/2;
if (list.get(mid)<=cur)
l = mid +1;
else
r=mid;
}
//如果cur是当前所有一出现元素的最大值,则直接放在链表的最后,否则的话插在对应的位置
if (l==list.size())
list.add(cur);
else
list.set(l,cur);
//因为二分查找寻找第一个大于cur的索引位置,因为索引位置是从0开始的,所以后面记录ans时需要额外加1
ans[i] += (l+1);
}
return ans;
}
}