五月训练 Day6

0. Leetcode 1984. 学生分数的最小差值

给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k 。
从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分 和 最低分 的 差值 达到 最小化 。
返回可能的 最小差值 。

分析与解答

由于选择 k 名学生时没有顺序要求,因此可以先排序,然后选择连续 k 名学生,以此确保每次选择 k 名学生后的最高分与最低分的差值是局部最小。遍历数组,得到全局最小。

class Solution {
public:
    int minimumDifference(vector<int>& nums, int k) {
        if (k == 1) {
            return 0;
        }

        sort(nums.begin(), nums.end()); // 排序
        // 排序后只需计算 i 与 i + k 数的差值
        int min(INT_MAX);
        for (int i = 0; i < nums.size() - k + 1; ++i) {
            int cur = nums[i + k - 1] - nums[i];
            if (min > cur) {
                min = cur;
            }
        }

        return min;
    }
};

排序时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN),遍历数组时间复杂度为 O ( N ) O(N) O(N),因此算法时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)

1. Leetcode 1876. 长度为三且各字符不同的子字符串

如果一个字符串不含有任何重复字符,我们称这个字符串为 好 字符串。
给你一个字符串 s ,请你返回 s 中长度为 3 的 好子字符串 的数量。
注意,如果相同的好子字符串出现多次,每一次都应该被记入答案之中。
子字符串 是一个字符串中连续的字符序列。

分析与解答

对字符串进行搜索,判断是否符合题意即可。

class Solution {
public:
    int countGoodSubstrings(string s) {
        if (s.size() < 3) {
            return 0;
        }

        int result(0);
        int l(0), r(l + 2);
        while (r < s.size()) {
            // 判断字串是否符合题意
            if (s[l] == s[l + 1]) {
                l++;
                r++;
            } else if (s[l + 1] == s[l + 2]) {
                l += 2;
                r += 2;
            } else if (s[l] == s[l + 2]) {
                l++;
                r++;
            } else {
                result++;
                l++;
                r++;
            }
        }

        return result;
    }
};

算法需要遍历数组,由于判断是否为 字串的时间复杂度为 O ( 1 ) O(1) O(1),因此算法时间复杂度为 O ( N ) O(N) O(N)

2. Leetcode 1839. 所有元音按顺序排布的最长子字符串

当一个字符串满足如下条件时,我们称它是 美丽的 :
所有 5 个英文元音字母(‘a’ ,‘e’ ,‘i’ ,‘o’ ,‘u’)都必须 至少 出现一次。
这些元音字母的顺序都必须按照 字典序 升序排布(也就是说所有的 ‘a’ 都在 ‘e’ 前面,所有的 ‘e’ 都在 ‘i’ 前面,以此类推)
比方说,字符串 “aeiou” 和 “aaaaaaeiiiioou” 都是 美丽的 ,但是 “uaeio” ,“aeoiu” 和 “aaaeeeooo” 不是美丽的 。
给你一个只包含英文元音字母的字符串 word ,请你返回 word 中 最长美丽子字符串的长度 。如果不存在这样的子字符串,请返回 0 。
子字符串 是字符串中一个连续的字符序列。

分析与解答

由于要找出顺序排列的子字符串,因此遍历字符串,使用两个指针记录符合条件的字串的首位置与尾位置。在当前搜索子串不满足条件时,移动首指针,并重置计数器;若当前子串搜索结束,通过首尾指针计算子串长度,并与当前最大值进行比较。

class Solution {
public:
    int longestBeautifulSubstring(string word) {
        int l(0), r(0), result(0);
        char set[5]{'a', 'e', 'i', 'o', 'u'};
        int curIdx(0);
        while (r < word.size()) {
            if (word[r] != set[curIdx]) { // 当前字符不是期望的字
                if (word[r] == 'a') { // 若当前字母为 'a',r 不变
                    curIdx = 0;
                    l = r;
                } else { // 若当前字母不为 'a',r 向后移
                    curIdx = 0;
                    l = r + 1;
                    r++;
                }
            } else { // 找到当前元音最后出现位置
                while (word[r] == set[curIdx]) {
                    r++;
                }
                curIdx = curIdx + 1;
                if (curIdx > 4) { // 循环完一个完美字符串
                    if (result < r - l) {
                        result = r - l;
                    }
                    l = r; // 循环结束移动首指针,否则字符串 aeiouaeiou 判断出错
                    curIdx = curIdx % 5;
                }
            }
        }
        return result;
    }
};

算法只需遍历一次数组,因此算法时间复杂度为 O ( N ) O(N) O(N)

3. Leetcode 1052. 爱生气的书店老板

有一个书店老板,他的书店开了 n 分钟。每分钟都有一些顾客进入这家商店。给定一个长度为 n 的整数数组 customers ,其中 customers[i] 是在第 i 分钟开始时进入商店的顾客数量,所有这些顾客在第 i 分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。
当书店老板生气时,那一分钟的顾客就会不满意,若老板不生气则顾客是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 minutes 分钟不生气,但却只能使用一次。
请你返回 这一天营业下来,最多有多少客户能够感到满意 。

分析与解答

首先按照默认情况计算满意顾客数量,之后以 minutes 为窗口长度,遍历 grumpy,将原本为 1 (即生气)时的顾客人数加到基础满意顾客数量上,并与最佳满意顾客数量比较更新。

class Solution {
public:
    int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int minutes) {
        int sun(0); // 正常情况下满意顾客数量
        int result(0);
        for (int i = 0; i < grumpy.size(); ++i) {
            if (grumpy[i] == 0) {
                sun += customers[i];
            }
        }
        result = sun; // 最差情况下结果
        
        for (int i = 0; i < grumpy.size() - minutes + 1; ++i) {
            int curR(sun);
            for (int j = 0; j < minutes; j++) { // 可能的增量
                if (grumpy[i + j] == 1) {
                    curR += customers[i + j];
                }
            }
            if (curR > result) { // 更新最佳答案
                result = curR;
            }
        }
        
        return result;
    }
};

算法需要遍历两次数组,因此算法时间复杂度为 O ( N ) O(N) O(N)

总结

滑动窗口与双指针很相像,但是窗口通常是搜索固定长度,即两个指针位置是相关的,而双指针中两个指针都很自由,通常通过判断是否满足条件决定移动方向与步长。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值