力扣(leetcode)每日一题 2516 每种字符至少取 K 个 | 滑动窗口

news/2024/9/28 7:44:19 标签: leetcode, 算法

2516. 每种字符至少取 K 个

给你一个由字符 'a''b''c' 组成的字符串 s 和一个非负整数 k 。每分钟,你可以选择取走 s 最左侧 还是 最右侧 的那个字符。

你必须取走每种字符 至少 k 个,返回需要的 最少 分钟数;如果无法取到,则返回 -1

示例 1:

**输入:**s = “aabaaaacaabc”, k = 2
**输出:**8
解释:
从 s 的左侧取三个字符,现在共取到两个字符 ‘a’ 、一个字符 ‘b’ 。
从 s 的右侧取五个字符,现在共取到四个字符 ‘a’ 、两个字符 ‘b’ 和两个字符 ‘c’ 。
共需要 3 + 5 = 8 分钟。
可以证明需要的最少分钟数是 8 。

示例 2:

**输入:**s = “a”, k = 1
输出:-1
**解释:**无法取到一个字符 ‘b’ 或者 ‘c’,所以返回 -1 。

提示:

  • 1 <= s.length <= 105
  • s 仅由字母 'a''b''c' 组成
  • 0 <= k <= s.length
题解

这里可以想到滑动窗口的变种。我左边一个指针,右边一个指针。左边的先顶到满足的最右边,然后右边顶向左边的同时,左边的指针可以弹出来
这个过程中不断刷新最小值


public static int takeCharacters(String s, int k) {  
    // 变相滑动窗口  
    int[] count = new int[3];  
    char[] charArray = s.toCharArray();  
    int index = 0;  
    int length = charArray.length;  
    for (int i = 0; i < length; i++) {  
        if (!verify(count, k)) {  
            if (charArray[i] == 'a') {  
                count[0]++;  
            } else if (charArray[i] == 'b') {  
                count[1]++;  
            } else if (charArray[i] == 'c') {  
                count[2]++;  
            }  
            index++;  
        } else {  
            break;  
        }  
    }  
    if (!verify(count, k)) {  
        return -1;  
    }  
    int res = index;// 个数  
    int rightIndex = 0;  
    for (int i = length - 1; i > 0 && index > 0 && rightIndex < res; i--) {  
        rightIndex++;  
        if (charArray[i] == 'a') {  
            count[0]++;  
        } else if (charArray[i] == 'b') {  
            count[1]++;  
        } else if (charArray[i] == 'c') {  
            count[2]++;  
        }  
        // 先加上最后面一位,然后弹出index的位数  
        while (verify(count, k) && index > 0) {  
            index--;  
            if (charArray[index] == 'a') {  
                count[0]--;  
            } else if (charArray[index] == 'b') {  
                count[1]--;  
            } else if (charArray[index] == 'c') {  
                count[2]--;  
            }  
  
        }  
        // 如果还是满足,就不需要加一  
        if (verify(count, k)) {  
            res = Math.min(res, index + rightIndex);  
        } else {  
            res = Math.min(res, index + rightIndex + 1);  
        }  
  
    }  
    return res;  
}  
  
public static boolean verify(int[] count, int k) {  
    return count[0] >= k && count[1] >= k && count[2] >= k;  
}

这里index就是左边的个数,leftIndex就是右边的个数。单独命名代码好写点。
退出语句皆是下 i > 0 && index > 0 && rightIndex < res i一定要大于0不说了,index如果是0,说明左指针已经弹完了,就可以退出了,还有如果右指针弹入的数量比刷新的值还大,也没有继续的意义了

这里的if是可以优化的



  
public static int takeCharacters2(String s, int k) {  
    // 变相滑动窗口  
    int[] count = new int[3];  
    char[] charArray = s.toCharArray();  
    int index = 0;  
    int length = charArray.length;  
    for (int i = 0; i < length; i++) {  
        if (!verify(count, k)) {  
            count[charArray[i] - 'a']++;  
            index++;  
        } else {  
            //index--;// 到了4更新  然后来到5发现成功了,其实index在4的时候已经成功了 这里减去1  
            break;  
        }  
    }  
    if (!verify(count, k)) {  
        return -1;  
    }  
    int res = index;// 个数  
    int rightIndex = 0;  
    for (int i = length - 1; i > 0 && index > 0 && rightIndex < res; i--) {  
        rightIndex++;  
        count[charArray[i] - 'a']++;  
        // 先加上最后面一位,然后弹出index的位数  
        while (verify(count, k) && index > 0) {  
            index--;  
            count[charArray[index] - 'a']--;  
        }  
        // 如果还是满足,就不需要加一  
        if (verify(count, k)) {  
            res = Math.min(res, index + rightIndex);  
        } else {  
            res = Math.min(res, index + rightIndex + 1);  
        }  
    }  
    return res;  
}

  
public static boolean verify(int[] count, int k) {  
    return count[0] >= k && count[1] >= k && count[2] >= k;  
}  
总结

其实滑动窗口的代码并不好写,涉及到临界的判断。如果是第一次写,很可能会自闭。我已经写过很多次了,还是不能一笔写完

请添加图片描述

这里花了这么多时间是想着把index和rightIndex融入到for循环。
然后这个忘记写了

 // 如果还是满足,就不需要加一  
        if (verify(count, k)) {  
            res = Math.min(res, index + rightIndex);  
        } else {  
            res = Math.min(res, index + rightIndex + 1);  
        }  

请添加图片描述

这不是最优解,但是最近比较忙,就不研究最优解了


http://www.niftyadmin.cn/n/5680671.html

相关文章

SpringBoot开发——使用Hutool工具包处理日期时间详解

文章目录 1、Hutool简介2、使用Hutool工具包进行日期时间处理2.1 添加依赖2.2 使用DateUtil类2.3 高级功能2.4. 优化指南1、Hutool简介 Hutool是一个功能丰富且易用的Java工具包,通过静态方法封装,降低相关API的学习成本,提高工作效率。它涵盖了字符串、数字、集合、编码、…

diffusion vs GAN

参考blog&#xff1a;深入浅出完整解析AIGC时代中GAN系列模型的前世今生与核心知识 diffusion vs GAN 对比 生成速度 GAN架构通常比Diffusion架构更快&#xff0c;因为GAN只需要一次前向传播即可生成样本&#xff0c;而Diffusion模型需要多次采样迭代来逐步生成最终图像。同时…

性能测试工具——JMeter

目录 一、JMeter介绍 1、下载安装JMeter 2、打开JMeter 方式一&#xff1a; 方式二&#xff1a; 3、JMeter基础设置 4、JMeter基本使用流程 &#xff08;1&#xff09;启动JMeter &#xff08;2&#xff09;在测试计划下添加线程组 &#xff08;3&#xff09;在 “线…

继承实现单例模式的探索(一)

前言 之前看到朋友采用继承的方式来实现单例模式&#xff0c;觉得很厉害&#xff0c;随后自己去探索了一番&#xff0c;以前实现单例模式都是把代码内联到具体的类中&#xff0c;这使得工程中每次需要使用单例模式时&#xff0c;都采用拷贝的方式&#xff0c;增加了很多冗余代码…

【算法】堆排之 215.数组中的第K个最大元素(medium)

系列专栏 双指针 模拟算法 分治思想 目录 1、题目链接 2、题目介绍 3、解法 解题思路 排序方法的选择&#xff1a; 构建小堆&#xff1a; 提取第 k 个最大元素&#xff1a; 4、代码 1、题目链接 215. 数组中的第K个最大元素 - 力扣&#xff08;LeetCode&#xff09;…

【hot100-java】【划分字母区间】

R9-贪心算法篇 印象题&#xff1a; 我记得&#xff0c;先用字典记录每个字母出现的下标&#xff0c;取出首个字母的下标j,然后我们for循环遍历一次&#xff0c;如果该下标大于 j&#xff0c;就要变化新的首字母&#xff0c;如果相等就说明一个字符串完成&#xff0c;如果小于就…

鸿蒙-app进入最近任务列表触发的监听

如果在UIAbility中&#xff0c;参考第一个链接&#xff0c;在页面中参考如下&#xff1a;State windowStage: window.WindowStage (getContext(this) as common.UIAbilityContext).windowStagetry {this.windowStage.on(windowStageEvent, (data) > {// 前台应用进入最近任…

Python PyQt5 在frame中生成多个QLabel控件和彻底销毁QLabel控件

文章目录 步骤 1: 创建主窗口和布局步骤 2: 添加QLabel到QFrame步骤 3: 销毁QLabel示例代码 在PyQt5中&#xff0c;在QFrame或任何其他容器控件中生成多个QLabel控件并通过一个标志位或方法来彻底销毁这些QLabel控件是相对直接的操作。以下是一个简单的示例&#xff0c;展示了如…