力扣5900. 蜡烛之间的盘子+5898. 数组中第 K 个独一无二的字符串

第九十三天 --- 力扣5900. 蜡烛之间的盘子+5898. 数组中第 K 个独一无二的字符串

题目一

力扣:5900. 蜡烛之间的盘子

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路:前缀和+二分搜索

1、我们经过读题之后,第一个事情就可以明确了,这其实就是一道,在给定区间内,计算*出现次数的题,也就是区间和,所以第一反应就是毋庸置疑的"前缀和"算法。

2、但是,这个题还有别的要求,就是你不能什么样的 " * " 都给计数了,必须是在两个" | “之间的✳才能被计数,所以在处理每次请求的的时候,他给的范围没用,不能直接用他给的范围通过前缀和来求✳个数,需要知道距离左边最近和右面最近的” | "位置,用这两个位置当左右范围,这样,求得个数才有用。

第一步:前缀和预处理

1、因为涉及到了区间和,一定记住,首选就是前缀和!
2、这个处理没啥说的,为了防止越界,从下标1开始,有✳就+1,反之+0.

第二部:二分法找" | "位置

1、这一步就是快速的找到他给的原始区间中最左端的和最右端的" | “位置,一说起快速查找,就一定要想到二分法!
2、想做到”1“中的操作,我得先知道各个” | “位置,我知道了全部的位置信息之后,在二分查找”1“中的两个值所以在预处理时候,遇到” | “,顺便记录位置。
3、在二分查找过程中,最重要的其实就是几步,别的都是根据题意而定。
<1>初始化的时候,找到左右边界
<2>循环能进行下去的条件就是左边界<=右边界
<3>每次循环都是先计算中间值,int mid = (l + r) / 2;
<4>中间值只需要考虑两种情况,成功找到目标,不成功找到目标
<5>最后紧承”<4>",在这两种情况中必做的就是左右边界的更新,别的操作根据题意即可
4、以上就是设计二分的方法,知道这个框架,直接将相应的题目套在框架上,就一定能正确的写出二分法。

代码

值得注意的是:在二分函数中,在查找成功后,即你找到的"|"位置在此次查询区间中,记录一下临时的答案,如果找最左侧的,就要把区间向左侧偏移,找最左的,如果找最右侧的,就把区间向右迁移即可。

class Solution {
    
    
public:
	vector<int> pos;// "|"位置数组
	int n;// pos大小
	int left;//每次查询的左边界
	int right;//每次查询的右边界

	int find(bool min_left) {
    
    //二分查找函数,参数是true代表查最左侧的,反之查最右侧的"|"
		int l = 0, r = n - 1;//初始化
		int ans = -1;//初始值,如果没找到就返回-1

		while (l <= r) {
    
    //循环条件
			int mid = (l + r) / 2;//找pos中间位置下标
			int pos_mid = pos[mid];//找到上述下标对应的s中位置

			if (pos_mid >= left && pos_mid <= right) {
    
    //成功
				ans = pos_mid;//记录答案
				if (min_left) {
    
    //找最左侧的"|",所以向左偏移
					r = mid - 1;
				}
				else {
    
    //找最右侧的"|",所以向右偏移
					l = mid + 1;
				}
			}
			else if (pos_mid < left) {
    
    //不成功1,找的位置低于查找区间,则区间向右偏移
				l = mid + 1;
			}
			else {
    
    //不成功1,找的位置高于查找区间,则区间向左偏移
				r = mid - 1;
			}
		}

		return ans;
	}

	vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
    
    
		int n_s = s.size();
		int n_queries = queries.size();

		vector<int> prefix(n_s + 1, 0);//前缀和
		vector<int> ans;//答案集合

		for (int i = 0; i < n_s; i++) {
    
    //预处理
			if (s[i] == '|') {
    
    
				pos.push_back(i);
				prefix[i + 1] += prefix[i];
				continue;
			}
			prefix[i + 1] += prefix[i] + 1;
		}

		n = pos.size();
		for (vector<int> v : queries) {
    
    
			left = v[0];//查询区间
			right = v[1];
			int min_left = find(true);//真实区间
			int min_right = find(false);

			if (min_left == -1 || min_right == -1) {
    
    //当"|"数量<2肯定出错,直接压栈0
				ans.push_back(0);
			}
			else {
    
    
				ans.push_back(prefix[min_right] - prefix[min_left]);
			}

		}

		return ans;
	}
};

所有代码均以通过力扣测试
(经过多次测试最短时间为):
在这里插入图片描述
时间复杂度:O(N)进行预处理,O(N)枚举需求,内部两次二分搜索,所以后半部分总共O(NlgN),所以总体的时间复杂度是O(NlgN)

Sum Up:
其实本题不难,重点考察的就是我们的基础编码能力和基础算法知识。

题目二

力扣:5898. 数组中第 K 个独一无二的字符串

在这里插入图片描述
在这里插入图片描述

思路

直接模拟就行,先用unordered_map记录一下各个字符串出现次数,然后再遍历一次,找到出现次数是1的,并且符合题意的字符串即可。

代码

class Solution {
    
    
public:
	unordered_map<string, int> item;
	string ans = "";
	string kthDistinct(vector<string>& arr, int k) {
    
    
		int n = arr.size();
		for (int i = 0; i < n; i++) {
    
    
			item[arr[i]]++;//计次数
		}
		for (int i = 0; i < n; i++) {
    
    
			if (item[arr[i]] == 1 && k) {
    
    
				k--;
				ans = arr[i];
			}
		}
		if (k > 0) {
    
    
			return "";
		}
		return ans;
	}
};

所有代码均以通过力扣测试
(经过多次测试最短时间为):
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_45678698/article/details/121086857
今日推荐