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

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
题解

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

这个过程中不断刷新最小值

java 复制代码
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是可以优化的

java 复制代码
  
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循环。

然后这个忘记写了

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

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

相关推荐
爱吃生蚝的于勒16 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~19 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
王哈哈^_^1 小时前
【数据集】【YOLO】【VOC】目标检测数据集,查找数据集,yolo目标检测算法详细实战训练步骤!
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·pyqt
星沁城1 小时前
240. 搜索二维矩阵 II
java·线性代数·算法·leetcode·矩阵
脉牛杂德1 小时前
多项式加法——C语言
数据结构·c++·算法
legend_jz1 小时前
STL--哈希
c++·算法·哈希算法
kingmax542120081 小时前
初三数学,最优解问题
算法
一直学习永不止步1 小时前
LeetCode题练习与总结:赎金信--383
java·数据结构·算法·leetcode·字符串·哈希表·计数
小刘|2 小时前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法
jjyangyou2 小时前
物联网核心安全系列——物联网安全需求
物联网·算法·安全·嵌入式·产品经理·硬件·产品设计