问题描述
小R在参观一个拥有 N 个商店的市场,每个商店包含一种类型的灯——红灯 R、绿灯 G 或蓝灯 B。市场中的商店由字符串 S 表示,其中每个字符对应商店的灯类型,商店的编号从 0 到 N-1。
小R可以从任意商店开始旅程,并在每分钟内从当前商店移动到下一个商店(i 到 i+1)。他需要收集每种类型的灯 R、G 和 B 各至少一个,并且他想在尽可能少的分钟内完成任务。
你的任务是帮助小R计算收集所有三种灯(R、G、B)所需的最少分钟数。如果无法收集到所有三种灯,返回 -1。
例如:当 N = 5 且 S = "RRGGB" 时,最少需要的时间是 3 分钟,因为从第一个商店(S[0])开始,移动 3 分钟后可以收集到所有三种灯。
测试样例
样例1:
输入:
N = 5,S = "RRGGB"
输出:3
样例2:
输入:
N = 4,S = "RRRR"
输出:-1
样例3:
输入:
N = 6,S = "RGBRGB"
输出:2
我们可以使用滑动窗口,在RGB三种灯都有的情况下循环移动和改变窗口大小,更新并记录窗口的最小值:
public class Main {
public static int solution(int N, String S) {
int[] rgb=new int[3];//三个元素分别代表RGB
int i,start,end,min;
for(i=0;i<N;i++){
add(rgb,S.charAt(i));
}
if(!check(rgb)){
return -1;
}
for(i=0;i<3;i++){
rgb[i]=0;
}
start=end=0;
//[start,end)里面的元素
min=Integer.MAX_VALUE;
while(end<N){
while(!check(rgb)&&end<N){
add(rgb,S.charAt(end++));
}
while(check(rgb)){
sub(rgb,S.charAt(start++));
}
min=Math.min(min,end-start);
}
return min;
}
//检查三种灯是否收集完
public static boolean check(int[] rgb){
return rgb[0]*rgb[1]*rgb[2]>0;
}
public static void add(int[] rgb, char ch){
switch (ch){
case 'R':{
rgb[0]++;
break;
}
case 'G':{
rgb[1]++;
break;
}
case 'B':{
rgb[2]++;
}
}
}
public static void sub(int[] rgb,char ch){
switch (ch){
case 'R':{
rgb[0]--;
break;
}
case 'G':{
rgb[1]--;
break;
}
case 'B':{
rgb[2]--;
}
}
}
public static void main(String[] args) {
System.out.println(solution(5, "RRGGB") == 3);
System.out.println(solution(4, "RRRR") == -1);
System.out.println(solution(6, "RGBRGB") == 2);
}
}
-
算法步骤:
- 首先遍历字符串
S,统计每种灯的数量,并检查是否包含所有三种灯。如果不包含,直接返回-1。 - 初始化
rgb数组,将start和end指针置于字符串的开始位置。 - 使用滑动窗口的思想,不断扩大
end指针,直到包含所有三种灯。然后尝试缩小start指针,寻找最小的窗口大小。 - 更新最小分钟数
min。 - 循环直到
end指针到达字符串末尾。
- 首先遍历字符串