算法刷题day4

Leetcode周赛第 416 场

题目1:3295. 举报垃圾信息

给你一个字符串数组 message 和一个字符串数组 bannedWords

如果数组中 至少 存在两个单词与 bannedWords 中的任一单词 完全相同,则该数组被视为 垃圾信息。如果数组 message 是垃圾信息,则返回 true;否则返回 false

示例 :

输入: message = ["hello","world","leetcode"], bannedWords = ["world","hello"]

输出: true

解释:数组 message 中的 "hello" 和 "world" 都出现在数组 bannedWords 中。

 思考:

        可以直接通过暴力循环,遍历message数组,同时遍历bannedWords查看该词是否出现,单次查询效率是O(m),若出现次数大于等于2则直接返回false。但随着样例规模的增大,肯定会超时。

        那么避免无效重复的循环查找最好的办法就是哈希表,我们创建一个哈希表,里面存储bannedWords数组中出现的词,这样查找单词的效率就是O(1)。

1.创建bannedWords的哈希表

初始化的值可以为int型或bool型,只是为了证明某个单词存在于数组中。

unordered_map<string, int> map;//key为string类型,value随便
   for(int i=0;i<bannedWords.size();i++){
      map[bannedWords[i]]=1;
   }
2.遍历message
int all=0;//message中禁词出现次数
for(int j=0;j<message.size();j++){
   if(map[message[j]]==1){
     all++;
   }
}
if(all>=2){
    return true;
}
return false;

完整可通过代码:

class Solution {
public:
    bool reportSpam(vector<string>& message, vector<string>& bannedWords) {
        unordered_map<string, int> map;
        for(int i=0;i<bannedWords.size();i++){
            map[bannedWords[i]]=1;
        }
        int al=0;
        for(int j=0;j<message.size();j++){
            if(map[message[j]]==1){
                al++;
            }
        }
        if(al>=2){
            return true;
        }
        return false;
    }
};

 题目2:3296. 移山所需的最少秒数

给你一个整数 mountainHeight 表示山的高度。同时给你一个整数数组 workerTimes,表示工人们的工作时间(单位:)。工人们需要 同时 进行工作以 降低 山的高度。

对于工人 i :

  • 山的高度降低 x,需要花费 workerTimes[i] + workerTimes[i] * 2 + ... + workerTimes[i] * x 秒。例如:
    • 山的高度降低 1,需要 workerTimes[i] 秒。
    • 山的高度降低 2,需要 workerTimes[i] + workerTimes[i] * 2 秒,依此类推。

返回一个整数,表示工人们使山的高度降低到 0 所需的 最少 秒数。

示例 1:

输入: mountainHeight = 4, workerTimes = [2,1,1]

输出: 3

解释:将山的高度降低到 0 的一种方式是:

  • 工人 0 将高度降低 1,花费 workerTimes[0] = 2 秒。
  • 工人 1 将高度降低 2,花费 workerTimes[1] + workerTimes[1] * 2 = 3 秒。
  • 工人 2 将高度降低 1,花费 workerTimes[2] = 1 秒。

因为工人同时工作,所需的最少时间为 max(2, 3, 1) = 3 秒。

第一遍看题目时没有看明白,就又看了一遍。大概意思是说:这里有一座山高h,有一堆工人,第i个工人第一次挖1m的山要workerTimes[i]的时间,第二次挖要2*workerTimes[i]......所有工人是可以并行工作的,问挖完整个山最少要多久?

最初的思路:1m接1m的把任务分配给每个工人,到最后再找出花费时间最大的工人,即题目所求。

遇到了一个问题:该以什么为条件选择该次挖掘的工人呢?(是workerTimes?是该工人当前花费时间?是工人下一次花费时间?)

想法:我们可以用一个数组next来存储:"若该次挖掘由第i位工人进行,则该工人总共的花费时间为next[i]"。

mountainHeight = 4, workerTimes = [2,1,1]

对于这个示例,勾造next[];

1.第一次挖掘:next[0]=2   next[1]=1   next[2]=1 (选择下标为1的工人 next[1]=1+2*1

2.第二次挖掘:next[0]=2   next[1]=3   next[2]=1 (选择下标为2的工人 next[2]=1+2*1

3.第三次挖掘:next[0]=2   next[1]=3   next[2]=3 (选择下标为0的工人 next[0]=2+2*2

4第四次挖掘:next[0]=6   next[1]=3   next[2]=3 (选择下标为1的工人 next[1]=3+3*1

最后maxTime=next[1]-3*1=4;

每次我们从next中挑最小的工人i来做此次挖掘,同时记得更新next[i]的值,可以额外开一个数组v来记录工人已经挖掘的次数

                                next[i]+=(该工人已挖次数+1)*workerTimes[i];

1.构造两个数组v和next
for(int i=0;i<workerTimes.size();i++){
    v.push_back(0);//每位工人挖的次数
    next.push_back(workerTimes[i]);
}
2.用while循环进行挖山工作

这里的min_element是寻找当前next中最小的数(此处为下文出问题埋下伏笔)

while(mountainHeight>0){
     minPosition = min_element(next.begin(),next.end()) -next.begin();//找到一个花费最小的人
     v[minPosition]++;//次数增加
     next[minPosition]+=(v[minPosition]+1)*workerTimes[minPosition];
     mountainHeight--;
     if(mountainHeight==0){
        break;
     }
}
3.找到next[i]-(v[i]+1)*workerTimes[i]最大的数

因为next中保存的是下一次挖掘,某个工人所需要耗的时间,但山已经挖完了,所以每个工人要减去下一次挖掘的时间,就可以得出真正的总花费。

for(int i=0;i<workerTimes.size();i++){
   num[i]-=v[i]*workerTimes[i];
}
long long maxValue = *max_element(num.begin(),num.end()); 
return maxValue;

完整代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include <numeric>
#include <string>
using namespace std;
class Solution {
public:
    long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
        vector<long long>num;
        long long p;
        long long k=workerTimes[0];
        long long q=mountainHeight;
        if(workerTimes.size()==1){
            p=k*(1+q)*q/2;
            return p;
        }
        vector<int>v;
        for(int i=0;i<workerTimes.size();i++){
            v.push_back(0);
            num.push_back(workerTimes[i]);
        }
        int minPosition;
        while(mountainHeight>0){
            minPosition = min_element(num.begin(),num.end()) - num.begin();//找到一个移除当前高度花费最小的人
            v[minPosition]++;//下次翻倍
            num[minPosition]+=(v[minPosition]+1)*workerTimes[minPosition];
            mountainHeight--;
            if(mountainHeight==0){
                break;
            }
        }
        for(int i=0;i<workerTimes.size();i++){
            num[i]-=(v[i]+1)*workerTimes[i];
        }
        long long maxValue = *max_element(num.begin(),num.end()); 
        return maxValue;
    }
};

恭喜,超时了!

emm,大量的时间被花费在寻找next数组中最小的数上了,时间复杂度为O(n),导致遇上较大测试集就会超时。如果用小顶堆是否可以更快速的实现找最小数呢。开干!直接使用了优先队列。

1.定义结构体
struct node
{
	long long x,y,z;//x即next中的数,y即工人在workerTimes中的下标,z即v中的数
	bool operator < (const node & a) const
	{
		return x>a.x;//小顶堆
	}
}nodes;
2.初始化队列
priority_queue<node>que;
   for(int i=0;i<workerTimes.size();i++){
      nodes.y=i;
      nodes.z=1;
      nodes.x=workerTimes[i]*(nodes.z);//x记录下一次让该位员工移山的总时间
      que.push(nodes);
}
3.移山
while(mountainHeight>0){
     node m=que.top();
     m.x+=(m.z+1)*workerTimes[m.y];
     m.z++;//下次翻倍
     que.pop();
     que.push(m);
     mountainHeight--;
     if(mountainHeight==0){
        break;
     }
}
4.找到耗时最大的人
for(int i=0;i<workerTimes.size();i++){
    node u=que.top();
    temp=u.x-u.z*workerTimes[u.y];
    if(temp>maxs){
       maxs=temp;
    }
    que.pop();
}

整体就是把上面的代码改成用优先队列来存了,唯一不同的是我直接给z初值赋为1了,这样就使nodes.x更新使避免了z+1的操作。

完整代码:
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include <numeric>
#include <string>
#include<queue>
using namespace std;
struct node
{
	long long x,y,z;
	bool operator < (const node & a) const
	{
		return x>a.x;
	}
}nodes;
class Solution {
public:
    long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
        vector<long long>num;
        long long p;
        long long k=workerTimes[0];
        long long q=mountainHeight;
        long long maxs=0;
        if(workerTimes.size()==1){
            p=k*(1+q)*q/2;
            return p;
        }
        priority_queue<node>que;
        for(int i=0;i<workerTimes.size();i++){
            nodes.y=i;
            nodes.z=1;
            nodes.x=workerTimes[i]*(nodes.z);//x记录下一次让该位员工移山的总时间
            que.push(nodes);
        }
        while(mountainHeight>0){
            node m=que.top();
            m.x+=(m.z+1)*workerTimes[m.y];
            m.z++;//下次翻倍
            que.pop();
            que.push(m);
            mountainHeight--;
            if(mountainHeight==0){
                break;
            }
        }
        long long temp;
        for(int i=0;i<workerTimes.size();i++){
            node u=que.top();
            temp=u.x-u.z*workerTimes[u.y];
            if(temp>maxs){
                maxs=temp;
                
            }
            que.pop();
        }
        return maxs;
    }
};

还有两题还没弄明白,留着明天做吧